diff options
Diffstat (limited to 'lldb/source')
143 files changed, 4675 insertions, 3073 deletions
diff --git a/lldb/source/API/SBFrame.cpp b/lldb/source/API/SBFrame.cpp index 5b69cf1..b12cfce 100644 --- a/lldb/source/API/SBFrame.cpp +++ b/lldb/source/API/SBFrame.cpp @@ -97,226 +97,176 @@ bool SBFrame::IsValid() const { } SBFrame::operator bool() const { LLDB_INSTRUMENT_VA(this); - - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) - return GetFrameSP().get() != nullptr; + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; } - // Without a target & process we can't have a valid stack frame. - return false; + return GetFrameSP().get() != nullptr; } SBSymbolContext SBFrame::GetSymbolContext(uint32_t resolve_scope) const { LLDB_INSTRUMENT_VA(this, resolve_scope); SBSymbolContext sb_sym_ctx; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope); - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - if (StackFrame *frame = exe_ctx.GetFramePtr()) - sb_sym_ctx = frame->GetSymbolContext(scope); - } + + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return sb_sym_ctx; } + SymbolContextItem scope = static_cast<SymbolContextItem>(resolve_scope); + if (StackFrame *frame = exe_ctx->GetFramePtr()) + sb_sym_ctx = frame->GetSymbolContext(scope); + return sb_sym_ctx; } SBModule SBFrame::GetModule() const { LLDB_INSTRUMENT_VA(this); - SBModule sb_module; - ModuleSP module_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - module_sp = frame->GetSymbolContext(eSymbolContextModule).module_sp; - sb_module.SetSP(module_sp); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBModule(); } + ModuleSP module_sp; + StackFrame *frame = exe_ctx->GetFramePtr(); + if (!frame) + return SBModule(); + + SBModule sb_module; + module_sp = frame->GetSymbolContext(eSymbolContextModule).module_sp; + sb_module.SetSP(module_sp); return sb_module; } SBCompileUnit SBFrame::GetCompileUnit() const { LLDB_INSTRUMENT_VA(this); - SBCompileUnit sb_comp_unit; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - sb_comp_unit.reset( - frame->GetSymbolContext(eSymbolContextCompUnit).comp_unit); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBCompileUnit(); } - return sb_comp_unit; + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return SBCompileUnit( + frame->GetSymbolContext(eSymbolContextCompUnit).comp_unit); + return SBCompileUnit(); } SBFunction SBFrame::GetFunction() const { LLDB_INSTRUMENT_VA(this); - SBFunction sb_function; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - sb_function.reset( - frame->GetSymbolContext(eSymbolContextFunction).function); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBFunction(); } - return sb_function; + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return SBFunction(frame->GetSymbolContext(eSymbolContextFunction).function); + return SBFunction(); } SBSymbol SBFrame::GetSymbol() const { LLDB_INSTRUMENT_VA(this); - SBSymbol sb_symbol; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - sb_symbol.reset(frame->GetSymbolContext(eSymbolContextSymbol).symbol); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBSymbol(); } - return sb_symbol; + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return SBSymbol(frame->GetSymbolContext(eSymbolContextSymbol).symbol); + return SBSymbol(); } SBBlock SBFrame::GetBlock() const { LLDB_INSTRUMENT_VA(this); - SBBlock sb_block; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) - sb_block.SetPtr(frame->GetSymbolContext(eSymbolContextBlock).block); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBBlock(); } - return sb_block; + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return SBBlock(frame->GetSymbolContext(eSymbolContextBlock).block); + return SBBlock(); } SBBlock SBFrame::GetFrameBlock() const { LLDB_INSTRUMENT_VA(this); - SBBlock sb_block; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) - sb_block.SetPtr(frame->GetFrameBlock()); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBBlock(); } - return sb_block; + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return SBBlock(frame->GetFrameBlock()); + return SBBlock(); } SBLineEntry SBFrame::GetLineEntry() const { LLDB_INSTRUMENT_VA(this); - SBLineEntry sb_line_entry; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - sb_line_entry.SetLineEntry( - frame->GetSymbolContext(eSymbolContextLineEntry).line_entry); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBLineEntry(); } - return sb_line_entry; + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return SBLineEntry( + &frame->GetSymbolContext(eSymbolContextLineEntry).line_entry); + return SBLineEntry(); } uint32_t SBFrame::GetFrameID() const { LLDB_INSTRUMENT_VA(this); - uint32_t frame_idx = UINT32_MAX; - - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + constexpr uint32_t error_frame_idx = UINT32_MAX; - StackFrame *frame = exe_ctx.GetFramePtr(); - if (frame) - frame_idx = frame->GetFrameIndex(); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return error_frame_idx; + } - return frame_idx; + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->GetFrameIndex(); + return error_frame_idx; } lldb::addr_t SBFrame::GetCFA() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return LLDB_INVALID_ADDRESS; + } - StackFrame *frame = exe_ctx.GetFramePtr(); - if (frame) + if (StackFrame *frame = exe_ctx->GetFramePtr()) return frame->GetStackID().GetCallFrameAddress(); return LLDB_INVALID_ADDRESS; } @@ -325,114 +275,86 @@ addr_t SBFrame::GetPC() const { LLDB_INSTRUMENT_VA(this); addr_t addr = LLDB_INVALID_ADDRESS; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - addr = frame->GetFrameCodeAddress().GetOpcodeLoadAddress( - target, AddressClass::eCode); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return addr; } + Target *target = exe_ctx->GetTargetPtr(); + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->GetFrameCodeAddress().GetOpcodeLoadAddress( + target, AddressClass::eCode); + return addr; } bool SBFrame::SetPC(addr_t new_pc) { LLDB_INSTRUMENT_VA(this, new_pc); - bool ret_val = false; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - if (StackFrame *frame = exe_ctx.GetFramePtr()) { - if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) { - ret_val = reg_ctx_sp->SetPC(new_pc); - } - } - } + constexpr bool error_ret_val = false; + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return error_ret_val; } - return ret_val; + if (StackFrame *frame = exe_ctx->GetFramePtr()) + if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) + return reg_ctx_sp->SetPC(new_pc); + + return error_ret_val; } addr_t SBFrame::GetSP() const { LLDB_INSTRUMENT_VA(this); - addr_t addr = LLDB_INVALID_ADDRESS; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - if (StackFrame *frame = exe_ctx.GetFramePtr()) { - if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) { - addr = reg_ctx_sp->GetSP(); - } - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return LLDB_INVALID_ADDRESS; } - return addr; + if (StackFrame *frame = exe_ctx->GetFramePtr()) + if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) + return reg_ctx_sp->GetSP(); + + return LLDB_INVALID_ADDRESS; } addr_t SBFrame::GetFP() const { LLDB_INSTRUMENT_VA(this); - addr_t addr = LLDB_INVALID_ADDRESS; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - if (StackFrame *frame = exe_ctx.GetFramePtr()) { - if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) { - addr = reg_ctx_sp->GetFP(); - } - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return LLDB_INVALID_ADDRESS; } - return addr; + if (StackFrame *frame = exe_ctx->GetFramePtr()) + if (RegisterContextSP reg_ctx_sp = frame->GetRegisterContext()) + return reg_ctx_sp->GetFP(); + + return LLDB_INVALID_ADDRESS; } SBAddress SBFrame::GetPCAddress() const { LLDB_INSTRUMENT_VA(this); - SBAddress sb_addr; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) - sb_addr.SetAddress(frame->GetFrameCodeAddress()); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBAddress(); } - return sb_addr; + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return SBAddress(frame->GetFrameCodeAddress()); + return SBAddress(); } void SBFrame::Clear() { @@ -445,12 +367,14 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path) { LLDB_INSTRUMENT_VA(this, var_path); SBValue sb_value; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return sb_value; + } - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); - if (frame && target) { + if (StackFrame *frame = exe_ctx->GetFramePtr()) { lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); sb_value = GetValueForVariablePath(var_path, use_dynamic); @@ -467,27 +391,22 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, return sb_value; } - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - VariableSP var_sp; - Status error; - ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath( - var_path, eNoDynamicValues, - StackFrame::eExpressionPathOptionCheckPtrVsMember | - StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, - var_sp, error)); - sb_value.SetSP(value_sp, use_dynamic); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return sb_value; + } + + if (StackFrame *frame = exe_ctx->GetFramePtr()) { + VariableSP var_sp; + Status error; + ValueObjectSP value_sp(frame->GetValueForVariableExpressionPath( + var_path, eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, + var_sp, error)); + sb_value.SetSP(value_sp, use_dynamic); } return sb_value; } @@ -495,18 +414,19 @@ lldb::SBValue SBFrame::GetValueForVariablePath(const char *var_path, SBValue SBFrame::FindVariable(const char *name) { LLDB_INSTRUMENT_VA(this, name); - SBValue value; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBValue(); + } - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); - if (frame && target) { + if (StackFrame *frame = exe_ctx->GetFramePtr()) { lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); - value = FindVariable(name, use_dynamic); + return FindVariable(name, use_dynamic); } - return value; + return SBValue(); } SBValue SBFrame::FindVariable(const char *name, @@ -520,26 +440,17 @@ SBValue SBFrame::FindVariable(const char *name, return sb_value; } - ValueObjectSP value_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - value_sp = frame->FindVariable(ConstString(name)); - - if (value_sp) - sb_value.SetSP(value_sp, use_dynamic); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return sb_value; } + if (StackFrame *frame = exe_ctx->GetFramePtr()) + if (ValueObjectSP value_sp = frame->FindVariable(ConstString(name))) + sb_value.SetSP(value_sp, use_dynamic); + return sb_value; } @@ -547,12 +458,14 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type) { LLDB_INSTRUMENT_VA(this, name, value_type); SBValue value; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return value; + } - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); - if (frame && target) { + if (StackFrame *frame = exe_ctx->GetFramePtr()) { lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); value = FindValue(name, value_type, use_dynamic); @@ -571,17 +484,17 @@ SBValue SBFrame::FindValue(const char *name, ValueType value_type, } ValueObjectSP value_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return value_sp; + } else { + Target *target = exe_ctx->GetTargetPtr(); + Process *process = exe_ctx->GetProcessPtr(); + if (target && process) { // FIXME: this check is redundant. + if (StackFrame *frame = exe_ctx->GetFramePtr()) { VariableList variable_list; switch (value_type) { @@ -698,10 +611,14 @@ bool SBFrame::operator!=(const SBFrame &rhs) const { SBThread SBFrame::GetThread() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBThread(); + } - ThreadSP thread_sp(exe_ctx.GetThreadSP()); + ThreadSP thread_sp(exe_ctx->GetThreadSP()); SBThread sb_thread(thread_sp); return sb_thread; @@ -710,19 +627,16 @@ SBThread SBFrame::GetThread() const { const char *SBFrame::Disassemble() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (!target || !process) + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); return nullptr; - - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - if (auto *frame = exe_ctx.GetFramePtr()) - return ConstString(frame->Disassemble()).GetCString(); } + if (auto *frame = exe_ctx->GetFramePtr()) + return ConstString(frame->Disassemble()).GetCString(); + return nullptr; } @@ -731,12 +645,15 @@ SBValueList SBFrame::GetVariables(bool arguments, bool locals, bool statics, LLDB_INSTRUMENT_VA(this, arguments, locals, statics, in_scope_only); SBValueList value_list; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return value_list; + } - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); - if (frame && target) { + if (StackFrame *frame = exe_ctx->GetFramePtr()) { + Target *target = exe_ctx->GetTargetPtr(); lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); const bool include_runtime_support_values = @@ -761,12 +678,16 @@ lldb::SBValueList SBFrame::GetVariables(bool arguments, bool locals, LLDB_INSTRUMENT_VA(this, arguments, locals, statics, in_scope_only, use_dynamic); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBValueList(); + } - Target *target = exe_ctx.GetTargetPtr(); + Target *target = exe_ctx->GetTargetPtr(); const bool include_runtime_support_values = - target ? target->GetDisplayRuntimeSupportValues() : false; + target->GetDisplayRuntimeSupportValues(); SBVariablesOptions options; options.SetIncludeArguments(arguments); options.SetIncludeLocals(locals); @@ -781,30 +702,26 @@ SBValueList SBFrame::GetVariables(const lldb::SBVariablesOptions &options) { LLDB_INSTRUMENT_VA(this, options); SBValueList value_list; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - - const bool statics = options.GetIncludeStatics(); - const bool arguments = options.GetIncludeArguments(); - const bool recognized_arguments = - options.GetIncludeRecognizedArguments(SBTarget(exe_ctx.GetTargetSP())); - const bool locals = options.GetIncludeLocals(); - const bool in_scope_only = options.GetInScopeOnly(); - const bool include_runtime_support_values = - options.GetIncludeRuntimeSupportValues(); - const lldb::DynamicValueType use_dynamic = options.GetUseDynamic(); - + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBValueList(); + } else { + const bool statics = options.GetIncludeStatics(); + const bool arguments = options.GetIncludeArguments(); + const bool recognized_arguments = + options.GetIncludeRecognizedArguments(SBTarget(exe_ctx->GetTargetSP())); + const bool locals = options.GetIncludeLocals(); + const bool in_scope_only = options.GetInScopeOnly(); + const bool include_runtime_support_values = + options.GetIncludeRuntimeSupportValues(); + const lldb::DynamicValueType use_dynamic = options.GetUseDynamic(); - std::set<VariableSP> variable_set; - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { + std::set<VariableSP> variable_set; + Process *process = exe_ctx->GetProcessPtr(); + if (process) { // FIXME: this check is redundant. + if (StackFrame *frame = exe_ctx->GetFramePtr()) { Debugger &dbg = process->GetTarget().GetDebugger(); VariableList *variable_list = nullptr; Status var_error; @@ -892,17 +809,16 @@ SBValueList SBFrame::GetRegisters() { LLDB_INSTRUMENT_VA(this); SBValueList value_list; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBValueList(); + } else { + Target *target = exe_ctx->GetTargetPtr(); + Process *process = exe_ctx->GetProcessPtr(); + if (target && process) { // FIXME: this check is redundant. + if (StackFrame *frame = exe_ctx->GetFramePtr()) { RegisterContextSP reg_ctx(frame->GetRegisterContext()); if (reg_ctx) { const uint32_t num_sets = reg_ctx->GetRegisterSetCount(); @@ -923,17 +839,16 @@ SBValue SBFrame::FindRegister(const char *name) { SBValue result; ValueObjectSP value_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBValue(); + } else { + Target *target = exe_ctx->GetTargetPtr(); + Process *process = exe_ctx->GetProcessPtr(); + if (target && process) { // FIXME: this check is redundant. + if (StackFrame *frame = exe_ctx->GetFramePtr()) { RegisterContextSP reg_ctx(frame->GetRegisterContext()); if (reg_ctx) { if (const RegisterInfo *reg_info = @@ -953,12 +868,11 @@ SBError SBFrame::GetDescriptionWithFormat(const SBFormat &format, SBStream &output) { Stream &strm = output.ref(); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) + return Status::FromError(exe_ctx.takeError()); - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); SBError error; if (!format) { @@ -966,16 +880,9 @@ SBError SBFrame::GetDescriptionWithFormat(const SBFormat &format, return error; } - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame && - frame->DumpUsingFormat(strm, format.GetFormatEntrySP().get())) { - return error; - } - } - } + if (StackFrame *frame = exe_ctx->GetFramePtr(); + frame && frame->DumpUsingFormat(strm, format.GetFormatEntrySP().get())) + return error; error.SetErrorStringWithFormat( "It was not possible to generate a frame " "description with the given format string '%s'", @@ -988,23 +895,16 @@ bool SBFrame::GetDescription(SBStream &description) { Stream &strm = description.ref(); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - frame->DumpUsingSettingsFormat(&strm); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + strm.PutCString("Error: process is not stopped."); + return true; + } - } else - strm.PutCString("No value"); + if (StackFrame *frame = exe_ctx->GetFramePtr()) + frame->DumpUsingSettingsFormat(&strm); return true; } @@ -1012,22 +912,24 @@ bool SBFrame::GetDescription(SBStream &description) { SBValue SBFrame::EvaluateExpression(const char *expr) { LLDB_INSTRUMENT_VA(this, expr); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return CreateProcessIsRunningExprEvalError(); + } - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); SBExpressionOptions options; - if (frame && target) { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) { lldb::DynamicValueType fetch_dynamic_value = frame->CalculateTarget()->GetPreferDynamicValue(); options.SetFetchDynamicValue(fetch_dynamic_value); } options.SetUnwindOnError(true); options.SetIgnoreBreakpoints(true); - SourceLanguage language; - if (target) - language = target->GetLanguage(); + Target *target = exe_ctx->GetTargetPtr(); + SourceLanguage language = target->GetLanguage(); if (!language && frame) language = frame->GetLanguage(); options.SetLanguage((SBSourceLanguageName)language.name, language.version); @@ -1043,14 +945,16 @@ SBFrame::EvaluateExpression(const char *expr, options.SetFetchDynamicValue(fetch_dynamic_value); options.SetUnwindOnError(true); options.SetIgnoreBreakpoints(true); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); - SourceLanguage language; - if (target) - language = target->GetLanguage(); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return CreateProcessIsRunningExprEvalError(); + } + + StackFrame *frame = exe_ctx->GetFramePtr(); + Target *target = exe_ctx->GetTargetPtr(); + SourceLanguage language = target->GetLanguage(); if (!language && frame) language = frame->GetLanguage(); options.SetLanguage((SBSourceLanguageName)language.name, language.version); @@ -1063,23 +967,35 @@ SBValue SBFrame::EvaluateExpression(const char *expr, LLDB_INSTRUMENT_VA(this, expr, fetch_dynamic_value, unwind_on_error); SBExpressionOptions options; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return CreateProcessIsRunningExprEvalError(); + } options.SetFetchDynamicValue(fetch_dynamic_value); options.SetUnwindOnError(unwind_on_error); options.SetIgnoreBreakpoints(true); - StackFrame *frame = exe_ctx.GetFramePtr(); - Target *target = exe_ctx.GetTargetPtr(); - SourceLanguage language; - if (target) - language = target->GetLanguage(); + StackFrame *frame = exe_ctx->GetFramePtr(); + Target *target = exe_ctx->GetTargetPtr(); + SourceLanguage language = target->GetLanguage(); if (!language && frame) language = frame->GetLanguage(); options.SetLanguage((SBSourceLanguageName)language.name, language.version); return EvaluateExpression(expr, options); } +lldb::SBValue SBFrame::CreateProcessIsRunningExprEvalError() { + auto error = Status::FromErrorString("can't evaluate expressions when the " + "process is running."); + ValueObjectSP expr_value_sp = + ValueObjectConstResult::Create(nullptr, std::move(error)); + SBValue expr_result; + expr_result.SetSP(expr_value_sp, false); + return expr_result; +} + lldb::SBValue SBFrame::EvaluateExpression(const char *expr, const SBExpressionOptions &options) { LLDB_INSTRUMENT_VA(this, expr, options); @@ -1094,18 +1010,16 @@ lldb::SBValue SBFrame::EvaluateExpression(const char *expr, ValueObjectSP expr_value_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + expr_result = CreateProcessIsRunningExprEvalError(); + } else { + Target *target = exe_ctx->GetTargetPtr(); + Process *process = exe_ctx->GetProcessPtr(); + if (target && process) { // FIXME: this check is redundant. + if (StackFrame *frame = exe_ctx->GetFramePtr()) { std::unique_ptr<llvm::PrettyStackTraceFormat> stack_trace; if (target->GetDisplayExpressionsInCrashlogs()) { StreamString frame_description; @@ -1122,16 +1036,10 @@ lldb::SBValue SBFrame::EvaluateExpression(const char *expr, } } else { Status error; - error = Status::FromErrorString("can't evaluate expressions when the " - "process is running."); - expr_value_sp = ValueObjectConstResult::Create(nullptr, std::move(error)); - expr_result.SetSP(expr_value_sp, false); - } - } else { - Status error; error = Status::FromErrorString("sbframe object is not valid."); expr_value_sp = ValueObjectConstResult::Create(nullptr, std::move(error)); expr_result.SetSP(expr_value_sp, false); + } } if (expr_result.GetError().Success()) @@ -1152,9 +1060,13 @@ SBStructuredData SBFrame::GetLanguageSpecificData() const { LLDB_INSTRUMENT_VA(this); SBStructuredData sb_data; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - StackFrame *frame = exe_ctx.GetFramePtr(); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return sb_data; + } + StackFrame *frame = exe_ctx->GetFramePtr(); if (!frame) return sb_data; @@ -1172,20 +1084,15 @@ bool SBFrame::IsInlined() { bool SBFrame::IsInlined() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) - return frame->IsInlined(); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; } + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->IsInlined(); return false; } @@ -1198,10 +1105,14 @@ bool SBFrame::IsArtificial() { bool SBFrame::IsArtificial() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } - if (StackFrame *frame = exe_ctx.GetFramePtr()) + if (StackFrame *frame = exe_ctx->GetFramePtr()) return frame->IsArtificial(); return false; @@ -1210,10 +1121,14 @@ bool SBFrame::IsArtificial() const { bool SBFrame::IsHidden() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } - if (StackFrame *frame = exe_ctx.GetFramePtr()) + if (StackFrame *frame = exe_ctx->GetFramePtr()) return frame->IsHidden(); return false; @@ -1228,63 +1143,44 @@ const char *SBFrame::GetFunctionName() { lldb::LanguageType SBFrame::GuessLanguage() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) { - return frame->GuessLanguage().AsLanguageType(); - } - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return eLanguageTypeUnknown; } + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->GuessLanguage().AsLanguageType(); return eLanguageTypeUnknown; } const char *SBFrame::GetFunctionName() const { LLDB_INSTRUMENT_VA(this); - const char *name = nullptr; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) - return frame->GetFunctionName(); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return nullptr; } - return name; + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->GetFunctionName(); + return nullptr; } const char *SBFrame::GetDisplayFunctionName() { LLDB_INSTRUMENT_VA(this); - const char *name = nullptr; - - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - StackFrame *frame = nullptr; - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) { - frame = exe_ctx.GetFramePtr(); - if (frame) - return frame->GetDisplayFunctionName(); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return nullptr; } - return name; + + if (StackFrame *frame = exe_ctx->GetFramePtr()) + return frame->GetDisplayFunctionName(); + return nullptr; } diff --git a/lldb/source/API/SBFunction.cpp b/lldb/source/API/SBFunction.cpp index 19861f6..65b02d6 100644 --- a/lldb/source/API/SBFunction.cpp +++ b/lldb/source/API/SBFunction.cpp @@ -79,6 +79,15 @@ const char *SBFunction::GetMangledName() const { return nullptr; } +const char *SBFunction::GetBaseName() const { + LLDB_INSTRUMENT_VA(this); + + if (!m_opaque_ptr) + return nullptr; + + return m_opaque_ptr->GetMangled().GetBaseName().AsCString(); +} + bool SBFunction::operator==(const SBFunction &rhs) const { LLDB_INSTRUMENT_VA(this, rhs); diff --git a/lldb/source/API/SBHostOS.cpp b/lldb/source/API/SBHostOS.cpp index a77a703..cd9b857 100644 --- a/lldb/source/API/SBHostOS.cpp +++ b/lldb/source/API/SBHostOS.cpp @@ -86,15 +86,7 @@ SBFileSpec SBHostOS::GetLLDBPath(lldb::PathType path_type) { SBFileSpec SBHostOS::GetUserHomeDirectory() { LLDB_INSTRUMENT(); - - FileSpec homedir; - FileSystem::Instance().GetHomeDirectory(homedir); - FileSystem::Instance().Resolve(homedir); - - SBFileSpec sb_fspec; - sb_fspec.SetFileSpec(homedir); - - return sb_fspec; + return HostInfo::GetUserHomeDir(); } lldb::thread_t SBHostOS::ThreadCreate(const char *name, diff --git a/lldb/source/API/SBModule.cpp b/lldb/source/API/SBModule.cpp index 985107e..5a57f45 100644 --- a/lldb/source/API/SBModule.cpp +++ b/lldb/source/API/SBModule.cpp @@ -671,3 +671,11 @@ void SBModule::GarbageCollectAllocatedModules() { const bool mandatory = false; ModuleList::RemoveOrphanSharedModules(mandatory); } + +const char *SBModule::GetObjectName() const { + LLDB_INSTRUMENT_VA(this); + + if (!m_opaque_sp) + return nullptr; + return m_opaque_sp->GetObjectName().AsCString(); +} diff --git a/lldb/source/API/SBStructuredData.cpp b/lldb/source/API/SBStructuredData.cpp index b891a34..8e2c18e 100644 --- a/lldb/source/API/SBStructuredData.cpp +++ b/lldb/source/API/SBStructuredData.cpp @@ -232,3 +232,47 @@ lldb::SBScriptObject SBStructuredData::GetGenericValue() const { return {m_impl_up->GetGenericValue(), eScriptLanguageDefault}; } + +void SBStructuredData::SetValueForKey(const char *key, + SBStructuredData &value) { + LLDB_INSTRUMENT_VA(this, key, value); + + if (StructuredData::ObjectSP obj_sp = value.m_impl_up->GetObjectSP()) + m_impl_up->SetValueForKey(key, obj_sp); +} + +void SBStructuredData::SetUnsignedIntegerValue(uint64_t value) { + LLDB_INSTRUMENT_VA(this, value); + + m_impl_up->SetUnsignedIntegerValue(value); +} + +void SBStructuredData::SetSignedIntegerValue(int64_t value) { + LLDB_INSTRUMENT_VA(this, value); + + m_impl_up->SetSignedIntegerValue(value); +} + +void SBStructuredData::SetFloatValue(double value) { + LLDB_INSTRUMENT_VA(this, value); + + m_impl_up->SetFloatValue(value); +} + +void SBStructuredData::SetBooleanValue(bool value) { + LLDB_INSTRUMENT_VA(this, value); + + m_impl_up->SetBooleanValue(value); +} + +void SBStructuredData::SetStringValue(const char *value) { + LLDB_INSTRUMENT_VA(this, value); + + m_impl_up->SetStringValue(value); +} + +void SBStructuredData::SetGenericValue(SBScriptObject value) { + LLDB_INSTRUMENT_VA(this, value); + + m_impl_up->SetGenericValue(value.GetPointer()); +} diff --git a/lldb/source/API/SBSymbol.cpp b/lldb/source/API/SBSymbol.cpp index 79477dd..3030c83 100644 --- a/lldb/source/API/SBSymbol.cpp +++ b/lldb/source/API/SBSymbol.cpp @@ -79,6 +79,15 @@ const char *SBSymbol::GetMangledName() const { return name; } +const char *SBSymbol::GetBaseName() const { + LLDB_INSTRUMENT_VA(this); + + if (!m_opaque_ptr) + return nullptr; + + return m_opaque_ptr->GetMangled().GetBaseName().AsCString(); +} + bool SBSymbol::operator==(const SBSymbol &rhs) const { LLDB_INSTRUMENT_VA(this, rhs); @@ -193,6 +202,14 @@ SymbolType SBSymbol::GetType() { return eSymbolTypeInvalid; } +uint32_t SBSymbol::GetID() { + LLDB_INSTRUMENT_VA(this); + + if (m_opaque_ptr) + return m_opaque_ptr->GetID(); + return 0; +} + bool SBSymbol::IsExternal() { LLDB_INSTRUMENT_VA(this); @@ -208,3 +225,23 @@ bool SBSymbol::IsSynthetic() { return m_opaque_ptr->IsSynthetic(); return false; } + +bool SBSymbol::IsDebug() { + LLDB_INSTRUMENT_VA(this); + + if (m_opaque_ptr) + return m_opaque_ptr->IsDebug(); + return false; +} + +const char *SBSymbol::GetTypeAsString(lldb::SymbolType symbol_type) { + LLDB_INSTRUMENT_VA(symbol_type); + + return Symbol::GetTypeAsString(symbol_type); +} + +lldb::SymbolType SBSymbol::GetTypeFromString(const char *str) { + LLDB_INSTRUMENT_VA(str); + + return Symbol::GetTypeFromString(str); +} diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index f26f795..eb56337 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -766,16 +766,19 @@ SBBreakpoint SBTarget::BreakpointCreateByName(const char *symbol_name, const bool hardware = false; const LazyBool skip_prologue = eLazyBoolCalculate; const lldb::addr_t offset = 0; + const bool offset_is_insn_count = false; if (module_name && module_name[0]) { FileSpecList module_spec_list; module_spec_list.Append(FileSpec(module_name)); sb_bp = target_sp->CreateBreakpoint( &module_spec_list, nullptr, symbol_name, eFunctionNameTypeAuto, - eLanguageTypeUnknown, offset, skip_prologue, internal, hardware); + eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue, + internal, hardware); } else { sb_bp = target_sp->CreateBreakpoint( nullptr, nullptr, symbol_name, eFunctionNameTypeAuto, - eLanguageTypeUnknown, offset, skip_prologue, internal, hardware); + eLanguageTypeUnknown, offset, offset_is_insn_count, skip_prologue, + internal, hardware); } } @@ -811,6 +814,17 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName( const SBFileSpecList &comp_unit_list) { LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language, module_list, comp_unit_list); + return BreakpointCreateByName(symbol_name, name_type_mask, symbol_language, 0, + false, module_list, comp_unit_list); +} + +lldb::SBBreakpoint SBTarget::BreakpointCreateByName( + const char *symbol_name, uint32_t name_type_mask, + LanguageType symbol_language, lldb::addr_t offset, + bool offset_is_insn_count, const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list) { + LLDB_INSTRUMENT_VA(this, symbol_name, name_type_mask, symbol_language, offset, + offset_is_insn_count, module_list, comp_unit_list); SBBreakpoint sb_bp; if (TargetSP target_sp = GetSP(); @@ -821,7 +835,8 @@ lldb::SBBreakpoint SBTarget::BreakpointCreateByName( std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex()); FunctionNameType mask = static_cast<FunctionNameType>(name_type_mask); sb_bp = target_sp->CreateBreakpoint(module_list.get(), comp_unit_list.get(), - symbol_name, mask, symbol_language, 0, + symbol_name, mask, symbol_language, + offset, offset_is_insn_count, skip_prologue, internal, hardware); } @@ -1555,6 +1570,18 @@ SBModule SBTarget::FindModule(const SBFileSpec &sb_file_spec) { return sb_module; } +SBModule SBTarget::FindModule(const SBModuleSpec &sb_module_spec) { + LLDB_INSTRUMENT_VA(this, sb_module_spec); + + SBModule sb_module; + if (TargetSP target_sp = GetSP(); target_sp && sb_module_spec.IsValid()) { + // The module list is thread safe, no need to lock. + sb_module.SetSP( + target_sp->GetImages().FindFirstModule(*sb_module_spec.m_opaque_up)); + } + return sb_module; +} + SBSymbolContextList SBTarget::FindCompileUnits(const SBFileSpec &sb_file_spec) { LLDB_INSTRUMENT_VA(this, sb_file_spec); @@ -1955,29 +1982,10 @@ lldb::SBInstructionList SBTarget::ReadInstructions(lldb::SBAddress base_addr, if (TargetSP target_sp = GetSP()) { if (Address *addr_ptr = base_addr.get()) { - DataBufferHeap data( - target_sp->GetArchitecture().GetMaximumOpcodeByteSize() * count, 0); - bool force_live_memory = true; - lldb_private::Status error; - lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; - const size_t bytes_read = - target_sp->ReadMemory(*addr_ptr, data.GetBytes(), data.GetByteSize(), - error, force_live_memory, &load_addr); - - const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; - if (!flavor_string || flavor_string[0] == '\0') { - // FIXME - we don't have the mechanism in place to do per-architecture - // settings. But since we know that for now we only support flavors on - // x86 & x86_64, - const llvm::Triple::ArchType arch = - target_sp->GetArchitecture().GetTriple().getArch(); - if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64) - flavor_string = target_sp->GetDisassemblyFlavor(); + if (llvm::Expected<DisassemblerSP> disassembler = + target_sp->ReadInstructions(*addr_ptr, count, flavor_string)) { + sb_instructions.SetDisassembler(*disassembler); } - sb_instructions.SetDisassembler(Disassembler::DisassembleBytes( - target_sp->GetArchitecture(), nullptr, flavor_string, - target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(), - *addr_ptr, data.GetBytes(), bytes_read, count, data_from_file)); } } diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp index 74bc66c..ec68b2a 100644 --- a/lldb/source/API/SBThread.cpp +++ b/lldb/source/API/SBThread.cpp @@ -90,16 +90,17 @@ lldb::SBQueue SBThread::GetQueue() const { SBQueue sb_queue; QueueSP queue_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - queue_sp = exe_ctx.GetThreadPtr()->GetQueue(); - if (queue_sp) { - sb_queue.SetQueue(queue_sp); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBQueue(); + } + + if (exe_ctx->HasThreadScope()) { + queue_sp = exe_ctx->GetThreadPtr()->GetQueue(); + if (queue_sp) { + sb_queue.SetQueue(queue_sp); } } @@ -112,19 +113,17 @@ bool SBThread::IsValid() const { } SBThread::operator bool() const { LLDB_INSTRUMENT_VA(this); + if (!m_opaque_sp) + return false; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - Target *target = exe_ctx.GetTargetPtr(); - Process *process = exe_ctx.GetProcessPtr(); - if (target && process) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&process->GetRunLock())) - return m_opaque_sp->GetThreadSP().get() != nullptr; + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; } - // Without a valid target & process, this thread can't be valid. - return false; + + return m_opaque_sp->GetThreadSP().get() != nullptr; } void SBThread::Clear() { @@ -137,29 +136,27 @@ StopReason SBThread::GetStopReason() { LLDB_INSTRUMENT_VA(this); StopReason reason = eStopReasonInvalid; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - return exe_ctx.GetThreadPtr()->GetStopReason(); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return reason; } + if (exe_ctx->HasThreadScope()) + return exe_ctx->GetThreadPtr()->GetStopReason(); + return reason; } size_t SBThread::GetStopReasonDataCount() { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (exe_ctx) { + if (exe_ctx->HasThreadScope()) { + StopInfoSP stop_info_sp = exe_ctx->GetThreadPtr()->GetStopInfo(); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); switch (reason) { @@ -179,7 +176,7 @@ size_t SBThread::GetStopReasonDataCount() { case eStopReasonBreakpoint: { break_id_t site_id = stop_info_sp->GetValue(); lldb::BreakpointSiteSP bp_site_sp( - exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID( + exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID( site_id)); if (bp_site_sp) return bp_site_sp->GetNumberOfConstituents() * 2; @@ -207,6 +204,9 @@ size_t SBThread::GetStopReasonDataCount() { } } } + } else { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return 0; } return 0; } @@ -214,13 +214,11 @@ size_t SBThread::GetStopReasonDataCount() { uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) { LLDB_INSTRUMENT_VA(this, idx); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - Thread *thread = exe_ctx.GetThreadPtr(); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (exe_ctx) { + if (exe_ctx->HasThreadScope()) { + Thread *thread = exe_ctx->GetThreadPtr(); StopInfoSP stop_info_sp = thread->GetStopInfo(); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); @@ -241,7 +239,7 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) { case eStopReasonBreakpoint: { break_id_t site_id = stop_info_sp->GetValue(); lldb::BreakpointSiteSP bp_site_sp( - exe_ctx.GetProcessPtr()->GetBreakpointSiteList().FindByID( + exe_ctx->GetProcessPtr()->GetBreakpointSiteList().FindByID( site_id)); if (bp_site_sp) { uint32_t bp_index = idx / 2; @@ -280,6 +278,9 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) { } } } + } else { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return 0; } return 0; } @@ -289,13 +290,17 @@ bool SBThread::GetStopReasonExtendedInfoAsJSON(lldb::SBStream &stream) { Stream &strm = stream.ref(); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } - if (!exe_ctx.HasThreadScope()) + if (!exe_ctx->HasThreadScope()) return false; - StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); + StopInfoSP stop_info = exe_ctx->GetThreadPtr()->GetStopInfo(); StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); if (!info) return false; @@ -311,15 +316,19 @@ SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) { SBThreadCollection threads; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBThreadCollection(); + } - if (!exe_ctx.HasThreadScope()) + if (!exe_ctx->HasThreadScope()) return SBThreadCollection(); - ProcessSP process_sp = exe_ctx.GetProcessSP(); + ProcessSP process_sp = exe_ctx->GetProcessSP(); - StopInfoSP stop_info = exe_ctx.GetThreadPtr()->GetStopInfo(); + StopInfoSP stop_info = exe_ctx->GetThreadPtr()->GetStopInfo(); StructuredData::ObjectSP info = stop_info->GetExtendedInfo(); if (!info) return threads; @@ -332,20 +341,20 @@ SBThread::GetStopReasonExtendedBacktraces(InstrumentationRuntimeType type) { size_t SBThread::GetStopDescription(char *dst, size_t dst_len) { LLDB_INSTRUMENT_VA(this, dst, dst_len); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - if (dst) *dst = 0; - if (!exe_ctx.HasThreadScope()) + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); return 0; + } - Process::StopLocker stop_locker; - if (!stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + if (!exe_ctx->HasThreadScope()) return 0; - std::string thread_stop_desc = exe_ctx.GetThreadPtr()->GetStopDescription(); + std::string thread_stop_desc = exe_ctx->GetThreadPtr()->GetStopDescription(); if (thread_stop_desc.empty()) return 0; @@ -361,16 +370,17 @@ SBValue SBThread::GetStopReturnValue() { LLDB_INSTRUMENT_VA(this); ValueObjectSP return_valobj_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBValue(); + } - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - StopInfoSP stop_info_sp = exe_ctx.GetThreadPtr()->GetStopInfo(); - if (stop_info_sp) { - return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp); - } + if (exe_ctx->HasThreadScope()) { + StopInfoSP stop_info_sp = exe_ctx->GetThreadPtr()->GetStopInfo(); + if (stop_info_sp) { + return_valobj_sp = StopInfo::GetReturnValueObject(stop_info_sp); } } @@ -402,47 +412,48 @@ uint32_t SBThread::GetIndexID() const { const char *SBThread::GetName() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (!exe_ctx.HasThreadScope()) + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); return nullptr; + } - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) - return ConstString(exe_ctx.GetThreadPtr()->GetName()).GetCString(); + if (!exe_ctx->HasThreadScope()) + return nullptr; - return nullptr; + return ConstString(exe_ctx->GetThreadPtr()->GetName()).GetCString(); } const char *SBThread::GetQueueName() const { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (!exe_ctx.HasThreadScope()) + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); return nullptr; + } - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) - return ConstString(exe_ctx.GetThreadPtr()->GetQueueName()).GetCString(); + if (!exe_ctx->HasThreadScope()) + return nullptr; - return nullptr; + return ConstString(exe_ctx->GetThreadPtr()->GetQueueName()).GetCString(); } lldb::queue_id_t SBThread::GetQueueID() const { LLDB_INSTRUMENT_VA(this); queue_id_t id = LLDB_INVALID_QUEUE_ID; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return id; + } - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - id = exe_ctx.GetThreadPtr()->GetQueueID(); - } + if (exe_ctx->HasThreadScope()) { + id = exe_ctx->GetThreadPtr()->GetQueueID(); } return id; @@ -452,13 +463,11 @@ bool SBThread::GetInfoItemByPathAsString(const char *path, SBStream &strm) { LLDB_INSTRUMENT_VA(this, path, strm); bool success = false; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - Thread *thread = exe_ctx.GetThreadPtr(); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (exe_ctx) { + if (exe_ctx->HasThreadScope()) { + Thread *thread = exe_ctx->GetThreadPtr(); StructuredData::ObjectSP info_root_sp = thread->GetExtendedInfo(); if (info_root_sp) { StructuredData::ObjectSP node = @@ -490,16 +499,16 @@ bool SBThread::GetInfoItemByPathAsString(const char *path, SBStream &strm) { } } } + } else { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return success; } return success; } -static Status ResumeNewPlan(ExecutionContext &exe_ctx, ThreadPlan *new_plan) { - Process *process = exe_ctx.GetProcessPtr(); - if (!process) - return Status::FromErrorString("No process in SBThread::ResumeNewPlan"); - +static Status ResumeNewPlan(StoppedExecutionContext exe_ctx, + ThreadPlan *new_plan) { Thread *thread = exe_ctx.GetThreadPtr(); if (!thread) return Status::FromErrorString("No thread in SBThread::ResumeNewPlan"); @@ -512,8 +521,11 @@ static Status ResumeNewPlan(ExecutionContext &exe_ctx, ThreadPlan *new_plan) { } // Why do we need to set the current thread by ID here??? + Process *process = exe_ctx.GetProcessPtr(); process->GetThreadList().SetSelectedThreadByID(thread->GetID()); + // Release the run lock but keep the API lock. + std::unique_lock<std::recursive_mutex> api_lock = exe_ctx.AllowResume(); if (process->GetTarget().GetDebugger().GetAsyncExecution()) return process->Resume(); return process->ResumeSynchronous(nullptr); @@ -529,15 +541,19 @@ void SBThread::StepOver(lldb::RunMode stop_other_threads) { void SBThread::StepOver(lldb::RunMode stop_other_threads, SBError &error) { LLDB_INSTRUMENT_VA(this, stop_other_threads, error); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + error = Status::FromError(exe_ctx.takeError()); + return; + } - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { error = Status::FromErrorString("this SBThread object is invalid"); return; } - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); bool abort_other_plans = false; StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0)); @@ -555,7 +571,7 @@ void SBThread::StepOver(lldb::RunMode stop_other_threads, SBError &error) { true, abort_other_plans, stop_other_threads, new_plan_status); } } - error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); } void SBThread::StepInto(lldb::RunMode stop_other_threads) { @@ -576,17 +592,21 @@ void SBThread::StepInto(const char *target_name, uint32_t end_line, SBError &error, lldb::RunMode stop_other_threads) { LLDB_INSTRUMENT_VA(this, target_name, end_line, error, stop_other_threads); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + error = Status::FromError(exe_ctx.takeError()); + return; + } - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { error = Status::FromErrorString("this SBThread object is invalid"); return; } bool abort_other_plans = false; - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); StackFrameSP frame_sp(thread->GetStackFrameAtIndex(0)); ThreadPlanSP new_plan_sp; Status new_plan_status; @@ -618,7 +638,7 @@ void SBThread::StepInto(const char *target_name, uint32_t end_line, } if (new_plan_status.Success()) - error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); else error = Status::FromErrorString(new_plan_status.AsCString()); } @@ -633,10 +653,14 @@ void SBThread::StepOut() { void SBThread::StepOut(SBError &error) { LLDB_INSTRUMENT_VA(this, error); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + error = Status::FromError(exe_ctx.takeError()); + return; + } - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { error = Status::FromErrorString("this SBThread object is invalid"); return; } @@ -644,7 +668,7 @@ void SBThread::StepOut(SBError &error) { bool abort_other_plans = false; bool stop_other_threads = false; - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); const LazyBool avoid_no_debug = eLazyBoolCalculate; Status new_plan_status; @@ -653,7 +677,7 @@ void SBThread::StepOut(SBError &error) { eVoteNoOpinion, 0, new_plan_status, avoid_no_debug)); if (new_plan_status.Success()) - error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); else error = Status::FromErrorString(new_plan_status.AsCString()); } @@ -668,8 +692,12 @@ void SBThread::StepOutOfFrame(SBFrame &sb_frame) { void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) { LLDB_INSTRUMENT_VA(this, sb_frame, error); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + error = Status::FromError(exe_ctx.takeError()); + return; + } if (!sb_frame.IsValid()) { error = Status::FromErrorString("passed invalid SBFrame object"); @@ -678,14 +706,14 @@ void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) { StackFrameSP frame_sp(sb_frame.GetFrameSP()); - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { error = Status::FromErrorString("this SBThread object is invalid"); return; } bool abort_other_plans = false; bool stop_other_threads = false; - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); if (sb_frame.GetThread().GetThreadID() != thread->GetID()) { error = Status::FromErrorString("passed a frame from another thread"); return; @@ -697,7 +725,7 @@ void SBThread::StepOutOfFrame(SBFrame &sb_frame, SBError &error) { eVoteNoOpinion, frame_sp->GetFrameIndex(), new_plan_status)); if (new_plan_status.Success()) - error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); else error = Status::FromErrorString(new_plan_status.AsCString()); } @@ -712,21 +740,25 @@ void SBThread::StepInstruction(bool step_over) { void SBThread::StepInstruction(bool step_over, SBError &error) { LLDB_INSTRUMENT_VA(this, step_over, error); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + error = Status::FromError(exe_ctx.takeError()); + return; + } - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { error = Status::FromErrorString("this SBThread object is invalid"); return; } - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); Status new_plan_status; ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepSingleInstruction( step_over, false, true, new_plan_status)); if (new_plan_status.Success()) - error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); else error = Status::FromErrorString(new_plan_status.AsCString()); } @@ -741,10 +773,14 @@ void SBThread::RunToAddress(lldb::addr_t addr) { void SBThread::RunToAddress(lldb::addr_t addr, SBError &error) { LLDB_INSTRUMENT_VA(this, addr, error); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + error = Status::FromError(exe_ctx.takeError()); + return; + } - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { error = Status::FromErrorString("this SBThread object is invalid"); return; } @@ -754,14 +790,14 @@ void SBThread::RunToAddress(lldb::addr_t addr, SBError &error) { Address target_addr(addr); - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); Status new_plan_status; ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress( abort_other_plans, target_addr, stop_other_threads, new_plan_status)); if (new_plan_status.Success()) - error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); else error = Status::FromErrorString(new_plan_status.AsCString()); } @@ -773,14 +809,16 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame, SBError sb_error; char path[PATH_MAX]; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) + return Status::FromError(exe_ctx.takeError()); StackFrameSP frame_sp(sb_frame.GetFrameSP()); - if (exe_ctx.HasThreadScope()) { - Target *target = exe_ctx.GetTargetPtr(); - Thread *thread = exe_ctx.GetThreadPtr(); + if (exe_ctx->HasThreadScope()) { + Target *target = exe_ctx->GetTargetPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); if (line == 0) { sb_error = Status::FromErrorString("invalid line argument"); @@ -875,7 +913,7 @@ SBError SBThread::StepOverUntil(lldb::SBFrame &sb_frame, frame_sp->GetFrameIndex(), new_plan_status)); if (new_plan_status.Success()) - sb_error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + sb_error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); else sb_error = Status::FromErrorString(new_plan_status.AsCString()); } @@ -907,15 +945,17 @@ SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, SBError error; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) + return Status::FromError(exe_ctx.takeError()); - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { error = Status::FromErrorString("this SBThread object is invalid"); return error; } - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); Status new_plan_status; StructuredData::ObjectSP obj_sp = args_data.m_impl_up->GetObjectSP(); @@ -931,7 +971,7 @@ SBError SBThread::StepUsingScriptedThreadPlan(const char *script_class_name, return error; if (new_plan_status.Success()) - error = ResumeNewPlan(exe_ctx, new_plan_sp.get()); + error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get()); else error = Status::FromErrorString(new_plan_status.AsCString()); @@ -943,15 +983,17 @@ SBError SBThread::JumpToLine(lldb::SBFileSpec &file_spec, uint32_t line) { SBError sb_error; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) + return Status::FromError(exe_ctx.takeError()); - if (!exe_ctx.HasThreadScope()) { + if (!exe_ctx->HasThreadScope()) { sb_error = Status::FromErrorString("this SBThread object is invalid"); return sb_error; } - Thread *thread = exe_ctx.GetThreadPtr(); + Thread *thread = exe_ctx->GetThreadPtr(); Status err = thread->JumpToLine(file_spec.ref(), line, true); sb_error.SetError(std::move(err)); @@ -963,11 +1005,13 @@ SBError SBThread::ReturnFromFrame(SBFrame &frame, SBValue &return_value) { SBError sb_error; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) + return Status::FromError(exe_ctx.takeError()); - if (exe_ctx.HasThreadScope()) { - Thread *thread = exe_ctx.GetThreadPtr(); + if (exe_ctx->HasThreadScope()) { + Thread *thread = exe_ctx->GetThreadPtr(); sb_error.SetError( thread->ReturnFromFrame(frame.GetFrameSP(), return_value.GetSP())); } @@ -980,11 +1024,13 @@ SBError SBThread::UnwindInnermostExpression() { SBError sb_error; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) + return Status::FromError(exe_ctx.takeError()); - if (exe_ctx.HasThreadScope()) { - Thread *thread = exe_ctx.GetThreadPtr(); + if (exe_ctx->HasThreadScope()) { + Thread *thread = exe_ctx->GetThreadPtr(); sb_error.SetError(thread->UnwindInnermostExpression()); if (sb_error.Success()) thread->SetSelectedFrameByIndex(0, false); @@ -1003,18 +1049,17 @@ bool SBThread::Suspend() { bool SBThread::Suspend(SBError &error) { LLDB_INSTRUMENT_VA(this, error); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + error = Status::FromError(exe_ctx.takeError()); + return false; + } bool result = false; - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - exe_ctx.GetThreadPtr()->SetResumeState(eStateSuspended); - result = true; - } else { - error = Status::FromErrorString("process is running"); - } + if (exe_ctx->HasThreadScope()) { + exe_ctx->GetThreadPtr()->SetResumeState(eStateSuspended); + result = true; } else error = Status::FromErrorString("this SBThread object is invalid"); return result; @@ -1030,19 +1075,19 @@ bool SBThread::Resume() { bool SBThread::Resume(SBError &error) { LLDB_INSTRUMENT_VA(this, error); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + error = Status::FromErrorString("process is running"); + return false; + } bool result = false; - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - const bool override_suspend = true; - exe_ctx.GetThreadPtr()->SetResumeState(eStateRunning, override_suspend); - result = true; - } else { - error = Status::FromErrorString("process is running"); - } + if (exe_ctx->HasThreadScope()) { + const bool override_suspend = true; + exe_ctx->GetThreadPtr()->SetResumeState(eStateRunning, override_suspend); + result = true; } else error = Status::FromErrorString("this SBThread object is invalid"); return result; @@ -1051,22 +1096,30 @@ bool SBThread::Resume(SBError &error) { bool SBThread::IsSuspended() { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } - if (exe_ctx.HasThreadScope()) - return exe_ctx.GetThreadPtr()->GetResumeState() == eStateSuspended; + if (exe_ctx->HasThreadScope()) + return exe_ctx->GetThreadPtr()->GetResumeState() == eStateSuspended; return false; } bool SBThread::IsStopped() { LLDB_INSTRUMENT_VA(this); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } - if (exe_ctx.HasThreadScope()) - return StateIsStoppedState(exe_ctx.GetThreadPtr()->GetState(), true); + if (exe_ctx->HasThreadScope()) + return StateIsStoppedState(exe_ctx->GetThreadPtr()->GetState(), true); return false; } @@ -1074,13 +1127,17 @@ SBProcess SBThread::GetProcess() { LLDB_INSTRUMENT_VA(this); SBProcess sb_process; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBProcess(); + } - if (exe_ctx.HasThreadScope()) { + if (exe_ctx->HasThreadScope()) { // Have to go up to the target so we can get a shared pointer to our // process... - sb_process.SetSP(exe_ctx.GetProcessSP()); + sb_process.SetSP(exe_ctx->GetProcessSP()); } return sb_process; @@ -1089,34 +1146,33 @@ SBProcess SBThread::GetProcess() { uint32_t SBThread::GetNumFrames() { LLDB_INSTRUMENT_VA(this); - uint32_t num_frames = 0; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - num_frames = exe_ctx.GetThreadPtr()->GetStackFrameCount(); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return 0; } - return num_frames; + if (exe_ctx->HasThreadScope()) + return exe_ctx->GetThreadPtr()->GetStackFrameCount(); + + return 0; } SBFrame SBThread::GetFrameAtIndex(uint32_t idx) { LLDB_INSTRUMENT_VA(this, idx); SBFrame sb_frame; - StackFrameSP frame_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBFrame(); + } - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - frame_sp = exe_ctx.GetThreadPtr()->GetStackFrameAtIndex(idx); - sb_frame.SetFrameSP(frame_sp); - } + if (exe_ctx->HasThreadScope()) { + StackFrameSP frame_sp = exe_ctx->GetThreadPtr()->GetStackFrameAtIndex(idx); + sb_frame.SetFrameSP(frame_sp); } return sb_frame; @@ -1126,17 +1182,17 @@ lldb::SBFrame SBThread::GetSelectedFrame() { LLDB_INSTRUMENT_VA(this); SBFrame sb_frame; - StackFrameSP frame_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - frame_sp = - exe_ctx.GetThreadPtr()->GetSelectedFrame(SelectMostRelevantFrame); - sb_frame.SetFrameSP(frame_sp); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBFrame(); + } + + if (exe_ctx->HasThreadScope()) { + StackFrameSP frame_sp = + exe_ctx->GetThreadPtr()->GetSelectedFrame(SelectMostRelevantFrame); + sb_frame.SetFrameSP(frame_sp); } return sb_frame; @@ -1147,18 +1203,19 @@ lldb::SBFrame SBThread::SetSelectedFrame(uint32_t idx) { SBFrame sb_frame; StackFrameSP frame_sp; - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); - - if (exe_ctx.HasThreadScope()) { - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - Thread *thread = exe_ctx.GetThreadPtr(); - frame_sp = thread->GetStackFrameAtIndex(idx); - if (frame_sp) { - thread->SetSelectedFrame(frame_sp.get()); - sb_frame.SetFrameSP(frame_sp); - } + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return SBFrame(); + } + + if (exe_ctx->HasThreadScope()) { + Thread *thread = exe_ctx->GetThreadPtr(); + frame_sp = thread->GetStackFrameAtIndex(idx); + if (frame_sp) { + thread->SetSelectedFrame(frame_sp.get()); + sb_frame.SetFrameSP(frame_sp); } } @@ -1202,12 +1259,16 @@ bool SBThread::GetStatus(SBStream &status) const { Stream &strm = status.ref(); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } - if (exe_ctx.HasThreadScope()) { - exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1, true, - /*show_hidden=*/true); + if (exe_ctx->HasThreadScope()) { + exe_ctx->GetThreadPtr()->GetStatus(strm, 0, 1, 1, true, + /*show_hidden=*/true); } else strm.PutCString("No status"); @@ -1225,11 +1286,15 @@ bool SBThread::GetDescription(SBStream &description, bool stop_format) const { Stream &strm = description.ref(); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return false; + } - if (exe_ctx.HasThreadScope()) { - exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat( + if (exe_ctx->HasThreadScope()) { + exe_ctx->GetThreadPtr()->DumpUsingSettingsFormat( strm, LLDB_INVALID_THREAD_ID, stop_format); } else strm.PutCString("No value"); @@ -1247,11 +1312,15 @@ SBError SBThread::GetDescriptionWithFormat(const SBFormat &format, return error; } - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); + if (!exe_ctx) { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); + return error; + } - if (exe_ctx.HasThreadScope()) { - if (exe_ctx.GetThreadPtr()->DumpUsingFormat( + if (exe_ctx->HasThreadScope()) { + if (exe_ctx->GetThreadPtr()->DumpUsingFormat( strm, LLDB_INVALID_THREAD_ID, format.GetFormatEntrySP().get())) { return error; } @@ -1267,17 +1336,15 @@ SBError SBThread::GetDescriptionWithFormat(const SBFormat &format, SBThread SBThread::GetExtendedBacktraceThread(const char *type) { LLDB_INSTRUMENT_VA(this, type); - std::unique_lock<std::recursive_mutex> lock; - ExecutionContext exe_ctx(m_opaque_sp.get(), lock); + llvm::Expected<StoppedExecutionContext> exe_ctx = + GetStoppedExecutionContext(m_opaque_sp); SBThread sb_origin_thread; - - Process::StopLocker stop_locker; - if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) { - if (exe_ctx.HasThreadScope()) { - ThreadSP real_thread(exe_ctx.GetThreadSP()); + if (exe_ctx) { + if (exe_ctx->HasThreadScope()) { + ThreadSP real_thread(exe_ctx->GetThreadSP()); if (real_thread) { ConstString type_const(type); - Process *process = exe_ctx.GetProcessPtr(); + Process *process = exe_ctx->GetProcessPtr(); if (process) { SystemRuntime *runtime = process->GetSystemRuntime(); if (runtime) { @@ -1293,6 +1360,8 @@ SBThread SBThread::GetExtendedBacktraceThread(const char *type) { } } } + } else { + LLDB_LOG_ERROR(GetLog(LLDBLog::API), exe_ctx.takeError(), "{0}"); } return sb_origin_thread; diff --git a/lldb/source/Breakpoint/BreakpointResolver.cpp b/lldb/source/Breakpoint/BreakpointResolver.cpp index 91fdff4..4ac4050 100644 --- a/lldb/source/Breakpoint/BreakpointResolver.cpp +++ b/lldb/source/Breakpoint/BreakpointResolver.cpp @@ -42,9 +42,9 @@ const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address", const char *BreakpointResolver::g_option_names[static_cast<uint32_t>( BreakpointResolver::OptionNames::LastOptionName)] = { - "AddressOffset", "Exact", "FileName", "Inlines", "Language", - "LineNumber", "Column", "ModuleName", "NameMask", "Offset", - "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth", + "AddressOffset", "Exact", "FileName", "Inlines", "Language", + "LineNumber", "Column", "ModuleName", "NameMask", "Offset", + "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth", "SkipPrologue", "SymbolNames"}; const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) { @@ -65,8 +65,10 @@ BreakpointResolver::NameToResolverTy(llvm::StringRef name) { BreakpointResolver::BreakpointResolver(const BreakpointSP &bkpt, const unsigned char resolverTy, - lldb::addr_t offset) - : m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {} + lldb::addr_t offset, + bool offset_is_insn_count) + : m_breakpoint(bkpt), m_offset(offset), + m_offset_is_insn_count(offset_is_insn_count), SubclassID(resolverTy) {} BreakpointResolver::~BreakpointResolver() = default; @@ -364,7 +366,32 @@ void BreakpointResolver::AddLocation(SearchFilter &filter, BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr, bool *new_location) { - loc_addr.Slide(m_offset); + if (m_offset_is_insn_count) { + Target &target = GetBreakpoint()->GetTarget(); + llvm::Expected<DisassemblerSP> expected_instructions = + target.ReadInstructions(loc_addr, m_offset); + if (!expected_instructions) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Breakpoints), + expected_instructions.takeError(), + "error: Unable to read instructions at address 0x{0:x}", + loc_addr.GetLoadAddress(&target)); + return BreakpointLocationSP(); + } + + const DisassemblerSP instructions = *expected_instructions; + if (!instructions || + instructions->GetInstructionList().GetSize() != m_offset) { + LLDB_LOG(GetLog(LLDBLog::Breakpoints), + "error: Unable to read {0} instructions at address 0x{1:x}", + m_offset, loc_addr.GetLoadAddress(&target)); + return BreakpointLocationSP(); + } + + loc_addr.Slide(instructions->GetInstructionList().GetTotalByteSize()); + } else { + loc_addr.Slide(m_offset); + } + return GetBreakpoint()->AddLocation(loc_addr, new_location); } diff --git a/lldb/source/Breakpoint/BreakpointResolverAddress.cpp b/lldb/source/Breakpoint/BreakpointResolverAddress.cpp index 828647c..70360d9 100644 --- a/lldb/source/Breakpoint/BreakpointResolverAddress.cpp +++ b/lldb/source/Breakpoint/BreakpointResolverAddress.cpp @@ -133,6 +133,11 @@ Searcher::CallbackReturn BreakpointResolverAddress::SearchCallback( Address tmp_address; if (module_sp->ResolveFileAddress(m_addr.GetOffset(), tmp_address)) m_addr = tmp_address; + else + return Searcher::eCallbackReturnStop; + } else { + // If we didn't find the module, then we can't resolve the address. + return Searcher::eCallbackReturnStop; } } diff --git a/lldb/source/Breakpoint/BreakpointResolverName.cpp b/lldb/source/Breakpoint/BreakpointResolverName.cpp index 21024a4..6372595 100644 --- a/lldb/source/Breakpoint/BreakpointResolverName.cpp +++ b/lldb/source/Breakpoint/BreakpointResolverName.cpp @@ -24,11 +24,13 @@ using namespace lldb; using namespace lldb_private; -BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt, - const char *name_cstr, FunctionNameType name_type_mask, - LanguageType language, Breakpoint::MatchType type, lldb::addr_t offset, +BreakpointResolverName::BreakpointResolverName( + const BreakpointSP &bkpt, const char *name_cstr, + FunctionNameType name_type_mask, LanguageType language, + Breakpoint::MatchType type, lldb::addr_t offset, bool offset_is_insn_count, bool skip_prologue) - : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset), + : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset, + offset_is_insn_count), m_match_type(type), m_language(language), m_skip_prologue(skip_prologue) { if (m_match_type == Breakpoint::Regexp) { m_regex = RegularExpression(name_cstr); @@ -81,7 +83,7 @@ BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt, BreakpointResolverName::BreakpointResolverName( const BreakpointResolverName &rhs) : BreakpointResolver(rhs.GetBreakpoint(), BreakpointResolver::NameResolver, - rhs.GetOffset()), + rhs.GetOffset(), rhs.GetOffsetIsInsnCount()), m_lookups(rhs.m_lookups), m_class_name(rhs.m_class_name), m_regex(rhs.m_regex), m_match_type(rhs.m_match_type), m_language(rhs.m_language), m_skip_prologue(rhs.m_skip_prologue) {} @@ -177,7 +179,8 @@ BreakpointResolverSP BreakpointResolverName::CreateFromStructuredData( std::shared_ptr<BreakpointResolverName> resolver_sp = std::make_shared<BreakpointResolverName>( nullptr, names[0].c_str(), name_masks[0], language, - Breakpoint::MatchType::Exact, offset, skip_prologue); + Breakpoint::MatchType::Exact, offset, + /*offset_is_insn_count = */ false, skip_prologue); for (size_t i = 1; i < num_elem; i++) { resolver_sp->AddNameLookup(ConstString(names[i]), name_masks[i]); } diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp index d7589cc..0d9eb45 100644 --- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp +++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp @@ -18,14 +18,11 @@ #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Target/StackFrame.h" #include "lldb/Utility/ConstString.h" -#include "lldb/Utility/LLDBLog.h" -#include "lldb/Utility/Log.h" #include "lldb/ValueObject/ValueObject.h" #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "llvm/ADT/StringRef.h" -#include "llvm/Support/Error.h" #include <regex> @@ -93,7 +90,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, dump_options.SetHideRootName(suppress_result) .SetExpandPointerTypeFlags(lldb::eTypeIsObjC); - bool is_po = m_varobj_options.use_objc; + bool is_po = m_varobj_options.use_object_desc; StackFrame *frame = m_exe_ctx.GetFramePtr(); @@ -135,22 +132,27 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, }; // Dump `valobj` according to whether `po` was requested or not. - auto dump_val_object = [&](ValueObject &valobj) -> Error { + auto dump_val_object = [&](ValueObject &valobj) { if (is_po) { StreamString temp_result_stream; - if (Error err = valobj.Dump(temp_result_stream, dump_options)) - return err; + 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 { - if (Error err = valobj.Dump(result.GetOutputStream(), dump_options)) - return err; + llvm::Error error = + valobj.Dump(result.GetOutputStream(), dump_options); + if (error) { + result.AppendError(toString(std::move(error))); + return; + } } m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(), m_cmd_name); result.SetStatus(eReturnStatusSuccessFinishResult); - return Error::success(); }; // First, try `expr` as a _limited_ frame variable expression path: only the @@ -184,13 +186,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, expr); } - Error err = dump_val_object(*valobj_sp); - if (!err) - return; - - // Dump failed, continue on to expression evaluation. - LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err), - "could not print frame variable '{1}': {0}", expr); + dump_val_object(*valobj_sp); + return; } } @@ -199,14 +196,8 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, if (auto *state = target.GetPersistentExpressionStateForLanguage(language)) if (auto var_sp = state->GetVariable(expr)) if (auto valobj_sp = var_sp->GetValueObject()) { - Error err = dump_val_object(*valobj_sp); - if (!err) - return; - - // Dump failed, continue on to expression evaluation. - LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err), - "could not print persistent variable '{1}': {0}", - expr); + dump_val_object(*valobj_sp); + return; } // Third, and lastly, try `expr` as a source expression to evaluate. @@ -257,12 +248,10 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, result.AppendNoteWithFormatv("ran `expression {0}{1}`", flags, expr); } - if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) { - if (Error err = dump_val_object(*valobj_sp)) - result.SetError(std::move(err)); - } else { + if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) + dump_val_object(*valobj_sp); + else result.SetStatus(eReturnStatusSuccessFinishNoResult); - } if (suppress_result) if (auto result_var_sp = diff --git a/lldb/source/Commands/CommandObjectDisassemble.cpp b/lldb/source/Commands/CommandObjectDisassemble.cpp index 70e687e..c0553d2 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.cpp +++ b/lldb/source/Commands/CommandObjectDisassemble.cpp @@ -154,6 +154,10 @@ Status CommandObjectDisassemble::CommandOptions::SetOptionValue( } } break; + case 'v': + enable_variable_annotations = true; + break; + case '\x01': force = true; break; @@ -180,6 +184,7 @@ void CommandObjectDisassemble::CommandOptions::OptionParsingStarting( end_addr = LLDB_INVALID_ADDRESS; symbol_containing_addr = LLDB_INVALID_ADDRESS; raw = false; + enable_variable_annotations = false; plugin_name.clear(); Target *target = @@ -503,8 +508,9 @@ void CommandObjectDisassemble::DoExecute(Args &command, "\"disassemble\" arguments are specified as options.\n"); const int terminal_width = GetCommandInterpreter().GetDebugger().GetTerminalWidth(); + const bool use_color = GetCommandInterpreter().GetDebugger().GetUseColor(); GetOptions()->GenerateOptionUsage(result.GetErrorStream(), *this, - terminal_width); + terminal_width, use_color); return; } @@ -528,6 +534,9 @@ void CommandObjectDisassemble::DoExecute(Args &command, if (m_options.raw) options |= Disassembler::eOptionRawOuput; + if (m_options.enable_variable_annotations) + options |= Disassembler::eOptionVariableAnnotations; + llvm::Expected<std::vector<AddressRange>> ranges = GetRangesForSelectedMode(result); if (!ranges) { diff --git a/lldb/source/Commands/CommandObjectDisassemble.h b/lldb/source/Commands/CommandObjectDisassemble.h index 4fbcd72..eed44ad 100644 --- a/lldb/source/Commands/CommandObjectDisassemble.h +++ b/lldb/source/Commands/CommandObjectDisassemble.h @@ -78,6 +78,7 @@ public: // in SetOptionValue if anything the selects a location is set. lldb::addr_t symbol_containing_addr = 0; bool force = false; + bool enable_variable_annotations = false; }; CommandObjectDisassemble(CommandInterpreter &interpreter); diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index c5b9167..197bffe9 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -202,7 +202,7 @@ EvaluateExpressionOptions CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions( const Target &target, const OptionGroupValueObjectDisplay &display_opts) { EvaluateExpressionOptions options; - options.SetCoerceToId(display_opts.use_objc); + options.SetCoerceToId(display_opts.use_object_desc); options.SetUnwindOnError(unwind_on_error); options.SetIgnoreBreakpoints(ignore_breakpoints); options.SetKeepInMemory(true); @@ -241,11 +241,11 @@ CommandObjectExpression::CommandOptions::GetEvaluateExpressionOptions( bool CommandObjectExpression::CommandOptions::ShouldSuppressResult( const OptionGroupValueObjectDisplay &display_opts) const { // Explicitly disabling persistent results takes precedence over the - // m_verbosity/use_objc logic. + // m_verbosity/use_object_desc logic. if (suppress_persistent_result != eLazyBoolCalculate) return suppress_persistent_result == eLazyBoolYes; - return display_opts.use_objc && + return display_opts.use_object_desc && m_verbosity == eLanguageRuntimeDescriptionDisplayVerbosityCompact; } @@ -332,7 +332,7 @@ Options *CommandObjectExpression::GetOptions() { return &m_option_group; } void CommandObjectExpression::HandleCompletion(CompletionRequest &request) { EvaluateExpressionOptions options; - options.SetCoerceToId(m_varobj_options.use_objc); + options.SetCoerceToId(m_varobj_options.use_object_desc); options.SetLanguage(m_command_options.language); options.SetExecutionPolicy(lldb_private::eExecutionPolicyNever); options.SetAutoApplyFixIts(false); diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index 5692699..7e58a95 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -349,7 +349,8 @@ protected: command[0].c_str()); m_options.GenerateOptionUsage( result.GetErrorStream(), *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); return; } diff --git a/lldb/source/Commands/CommandObjectProtocolServer.cpp b/lldb/source/Commands/CommandObjectProtocolServer.cpp index f11e27f..c5ab9e9 100644 --- a/lldb/source/Commands/CommandObjectProtocolServer.cpp +++ b/lldb/source/Commands/CommandObjectProtocolServer.cpp @@ -15,6 +15,7 @@ #include "lldb/Utility/UriParser.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/FormatAdapters.h" +#include <string> using namespace llvm; using namespace lldb; @@ -28,7 +29,7 @@ public: CommandObjectProtocolServerStart(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "protocol-server start", "start protocol server", - "protocol-server start <protocol> <connection>") { + "protocol-server start <protocol> [<connection>]") { AddSimpleArgumentList(lldb::eArgTypeProtocol, eArgRepeatPlain); AddSimpleArgumentList(lldb::eArgTypeConnectURL, eArgRepeatPlain); } @@ -51,15 +52,13 @@ protected: return; } - if (args.GetArgumentCount() < 2) { - result.AppendError("no connection specified"); - return; - } - llvm::StringRef connection_uri = args.GetArgumentAtIndex(1); + std::string connection_uri = "listen://[localhost]:0"; + if (args.GetArgumentCount() >= 2) + connection_uri = args.GetArgumentAtIndex(1); const char *connection_error = - "unsupported connection specifier, expected 'accept:///path' or " - "'listen://[host]:port', got '{0}'."; + "unsupported connection specifier, expected 'accept:///path' " + "or 'listen://[host]:port', got '{0}'."; auto uri = lldb_private::URI::Parse(connection_uri); if (!uri) { result.AppendErrorWithFormatv(connection_error, connection_uri); diff --git a/lldb/source/Commands/CommandObjectSettings.cpp b/lldb/source/Commands/CommandObjectSettings.cpp index 7bbb0dd..126f57c 100644 --- a/lldb/source/Commands/CommandObjectSettings.cpp +++ b/lldb/source/Commands/CommandObjectSettings.cpp @@ -237,28 +237,62 @@ private: }; // CommandObjectSettingsShow -- Show current values +#define LLDB_OPTIONS_settings_show +#include "CommandOptions.inc" class CommandObjectSettingsShow : public CommandObjectParsed { public: CommandObjectSettingsShow(CommandInterpreter &interpreter) : CommandObjectParsed(interpreter, "settings show", "Show matching debugger settings and their current " - "values. Defaults to showing all settings.", - nullptr) { + "values. Defaults to showing all settings.") { AddSimpleArgumentList(eArgTypeSettingVariableName, eArgRepeatOptional); } ~CommandObjectSettingsShow() override = default; + Options *GetOptions() override { return &m_options; } + + class CommandOptions : public Options { + public: + ~CommandOptions() override = default; + + Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, + ExecutionContext *execution_context) override { + const int short_option = m_getopt_table[option_idx].val; + switch (short_option) { + case 'd': + m_include_defaults = true; + break; + default: + llvm_unreachable("Unimplemented option"); + } + return {}; + } + + void OptionParsingStarting(ExecutionContext *execution_context) override { + m_include_defaults = false; + } + + llvm::ArrayRef<OptionDefinition> GetDefinitions() override { + return g_settings_show_options; + } + + bool m_include_defaults = false; + }; + protected: void DoExecute(Args &args, CommandReturnObject &result) override { result.SetStatus(eReturnStatusSuccessFinishResult); + uint32_t dump_mask = OptionValue::eDumpGroupValue; + if (m_options.m_include_defaults) + dump_mask |= OptionValue::eDumpOptionDefaultValue; + if (!args.empty()) { for (const auto &arg : args) { Status error(GetDebugger().DumpPropertyValue( - &m_exe_ctx, result.GetOutputStream(), arg.ref(), - OptionValue::eDumpGroupValue)); + &m_exe_ctx, result.GetOutputStream(), arg.ref(), dump_mask)); if (error.Success()) { result.GetOutputStream().EOL(); } else { @@ -267,9 +301,12 @@ protected: } } else { GetDebugger().DumpAllPropertyValues(&m_exe_ctx, result.GetOutputStream(), - OptionValue::eDumpGroupValue); + dump_mask); } } + +private: + CommandOptions m_options; }; // CommandObjectSettingsWrite -- Write settings to file diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index dbebbbd..3ae08de 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -4075,7 +4075,8 @@ public: default: m_options.GenerateOptionUsage( result.GetErrorStream(), *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); syntax_error = true; break; } diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td index acb7410..4a70e55 100644 --- a/lldb/source/Commands/Options.td +++ b/lldb/source/Commands/Options.td @@ -56,6 +56,11 @@ let Command = "settings clear" in { Desc<"Clear all settings.">; } +let Command = "settings show" in { + def setshow_defaults : Option<"defaults", "d">, + Desc<"Include default values if defined.">; +} + let Command = "breakpoint list" in { // FIXME: We need to add an "internal" command, and then add this sort of // thing to it. But I need to see it for now, and don't want to wait. @@ -79,7 +84,7 @@ let Command = "breakpoint modify" in { Desc<"Set the number of times this breakpoint is skipped before stopping.">; def breakpoint_modify_one_shot : Option<"one-shot", "o">, Group<1>, Arg<"Boolean">, - Desc<"The breakpoint is deleted the first time it stop causes a stop.">; + Desc<"The breakpoint is deleted the first time it causes a stop.">; def breakpoint_modify_thread_index : Option<"thread-index", "x">, Group<1>, Arg<"ThreadIndex">, Desc<"The breakpoint stops only for the thread whose " "index matches this argument.">; @@ -205,7 +210,7 @@ let Command = "breakpoint set" in { "identifiers). If not set the target.language setting is used.">; def breakpoint_set_skip_prologue : Option<"skip-prologue", "K">, Arg<"Boolean">, Groups<[1,3,4,5,6,7,8,12]>, - Desc<"sKip the prologue if the breakpoint is at the beginning of a " + Desc<"Skip the prologue if the breakpoint is at the beginning of a " "function. If not set the target.skip-prologue setting is used.">; def breakpoint_set_breakpoint_name : Option<"breakpoint-name", "N">, Arg<"BreakpointName">, @@ -245,9 +250,12 @@ let Command = "breakpoint delete" in { def breakpoint_delete_dummy_breakpoints : Option<"dummy-breakpoints", "D">, Group<1>, Desc<"Delete Dummy breakpoints - i.e. breakpoints set before a " "file is provided, which prime new targets.">; - def breakpoint_delete_disabled : Option<"disabled", "d">, Group<1>, - Desc<"Delete all breakpoints which are currently disabled. When using the disabled option " - "any breakpoints listed on the command line are EXCLUDED from deletion.">; + def breakpoint_delete_disabled + : Option<"disabled", "d">, + Group<1>, + Desc<"Delete all breakpoints which are currently disabled. When using " + "the disabled option any breakpoints listed on the command line " + "are EXCLUDED from deletion.">; } let Command = "breakpoint name" in { @@ -337,8 +345,10 @@ let Command = "disassemble" in { Desc<"Override the CPU for disassembling.">; def disassemble_options_features : Option<"features", "Y">, Arg<"CPUFeatures">, Desc<"Specify additional CPU features for disassembling.">; - def disassemble_options_arch : Option<"arch", "A">, Arg<"Architecture">, - Desc<"Specify the architecture to use from cross disassembly.">; + def disassemble_options_arch + : Option<"arch", "A">, + Arg<"Architecture">, + Desc<"Specify the architecture to use for cross disassembly.">; def disassemble_options_start_address : Option<"start-address", "s">, Groups<[1,2]>, Arg<"AddressOrExpression">, Required, Desc<"Address at which to start disassembling.">; @@ -361,6 +371,8 @@ let Command = "disassemble" in { Desc<"Disassemble function containing this address.">; def disassemble_options_force : Option<"force", "\\x01">, Groups<[2,3,4,5,7]>, Desc<"Force disassembly of large functions.">; + def disassemble_options_variable : Option<"variable", "v">, + Desc<"Enable variable disassembly annotations for this invocation.">; } let Command = "diagnostics dump" in { @@ -441,13 +453,17 @@ let Command = "frame recognizer add" in { Desc<"Give the name of a Python class to use for this frame recognizer.">; def frame_recognizer_regex : Option<"regex", "x">, Desc<"Function name and module name are actually regular expressions.">; - def frame_recognizer_first_instruction_only : Option<"first-instruction-only", "f">, Arg<"Boolean">, - Desc<"If true, only apply this recognizer to frames whose PC currently points to the " - "first instruction of the specified function. If false, the recognizer " - "will always be applied, regardless of the current position within the specified function. The " - "implementer should keep in mind that some features, e.g. accessing function argument " - "values via $arg<N>, are not guaranteed to work reliably in this case, so extra care must " - "be taken to make the recognizer operate correctly. Defaults to true.">; + def frame_recognizer_first_instruction_only + : Option<"first-instruction-only", "f">, + Arg<"Boolean">, + Desc<"If true, only apply this recognizer to frames whose PC currently " + "points to the first instruction of the specified function. If " + "false, the recognizer will always be applied, regardless of the " + "current position within the specified function. The implementer " + "should keep in mind that some features, e.g., accessing function " + "argument values via $arg<N>, are not guaranteed to work reliably " + "in this case, so extra care must be taken to make the recognizer " + "operate correctly. Defaults to true.">; } let Command = "history" in { @@ -665,9 +681,10 @@ let Command = "platform process list" in { def platform_process_list_show_args : Option<"show-args", "A">, GroupRange<1, 6>, Desc<"Show process arguments instead of the process executable basename.">; - def platform_process_list_all_users: Option<"all-users", "x">, - GroupRange<1,6>, - Desc<"Show processes matching all user IDs.">; + def platform_process_list_all_users + : Option<"all-users", "x">, + GroupRange<1, 6>, + Desc<"Show processes matching all user IDs.">; def platform_process_list_verbose : Option<"verbose", "v">, GroupRange<1, 6>, Desc<"Enable verbose output.">; } @@ -737,17 +754,31 @@ let Command = "process launch" in { let Command = "process attach" in { def process_attach_continue : Option<"continue", "c">, - Desc<"Immediately continue the process once attached.">; - def process_attach_plugin : Option<"plugin", "P">, Arg<"Plugin">, - Desc<"Name of the process plugin you want to use.">; - def process_attach_pid : Option<"pid", "p">, Group<1>, Arg<"Pid">, - Desc<"The process ID of an existing process to attach to.">; - def process_attach_name : Option<"name", "n">, Group<2>, Arg<"ProcessName">, - Desc<"The name of the process to attach to.">; - def process_attach_include_existing : Option<"include-existing", "i">, - Group<2>, Desc<"Include existing processes when doing attach -w.">; - def process_attach_waitfor : Option<"waitfor", "w">, Group<2>, - Desc<"Wait for the process with <process-name> to launch.">; + Desc<"Immediately ${c}ontinue the process " + "once attached.">; + def process_attach_plugin : Option<"plugin", "P">, + Arg<"Plugin">, + Desc<"Name of the process ${p}lugin you " + "want to use.">; + def process_attach_pid : Option<"pid", "p">, + Group<1>, + Arg<"Pid">, + Desc<"The ${p}rocess ID " + "of an existing process to attach to.">; + def process_attach_name : Option<"name", "n">, + Group<2>, + Arg<"ProcessName">, + Desc<"The ${n}ame of " + "the process to attach to.">; + def process_attach_include_existing + : Option<"include-existing", "i">, + Group<2>, + Desc<"${I}nclude existing processes when " + "doing attach -w.">; + def process_attach_waitfor : Option<"waitfor", "w">, + Group<2>, + Desc<"${W}ait for the process with " + "<process-name> to launch.">; } let Command = "process continue" in { @@ -905,8 +936,11 @@ let Command = "source list" in { "indicate valid places to set source level breakpoints.">; def source_list_file : Option<"file", "f">, Group<1>, Arg<"Filename">, Completion<"SourceFile">, Desc<"The file from which to display source.">; - def source_list_line : Option<"line", "l">, Group<1>, Arg<"LineNum">, - Desc<"The line number at which to start the display source.">; + def source_list_line + : Option<"line", "l">, + Group<1>, + Arg<"LineNum">, + Desc<"The line number at which to start displaying source.">; def source_list_name : Option<"name", "n">, Group<2>, Arg<"Symbol">, Completion<"Symbol">, Desc<"The name of a function whose source to display.">; @@ -1068,12 +1102,14 @@ let Command = "target stop hook add" in { def target_stop_hook_add_auto_continue : Option<"auto-continue", "G">, Arg<"Boolean">, Desc<"The stop-hook will auto-continue after running its" " commands.">; - def target_stop_hook_add_at_initial_stop : Option<"at-initial-stop", "I">, - Arg<"Boolean">, Desc<"Whether the stop-hook will trigger when lldb " - "initially gains control of the process. For a process launch, this " - "initial stop may happen very early on - before the loader has run. You " - "can use this option if you do not want some stop-hooks to run then. " - "Defaults to true.">; + def target_stop_hook_add_at_initial_stop + : Option<"at-initial-stop", "I">, + Arg<"Boolean">, + Desc<"Whether the stop-hook will trigger when lldb " + "initially gains control of the process. For a process launch, " + "this initial stop may happen very early on - before the loader " + "has run. You can use this option if you do not want some " + "stop-hooks to run then. Defaults to true.">; } let Command = "thread backtrace" in { @@ -1099,12 +1135,16 @@ let Command = "thread step scope" in { def thread_step_scope_count : Option<"count", "c">, Group<1>, Arg<"Count">, Desc<"How many times to perform the stepping operation - currently only " "supported for step-inst and next-inst.">; - def thread_step_scope_end_linenumber : Option<"end-linenumber", "e">, - Group<1>, Arg<"LineNum">, Desc<"The line at which to stop stepping - " - "defaults to the next line and only supported for step-in and step-over." - " You can also pass the string 'block' to step to the end of the current" - " block. This is particularly use in conjunction with --step-target to" - " step through a complex calling sequence.">; + def thread_step_scope_end_linenumber + : Option<"end-linenumber", "e">, + Group<1>, + Arg<"LineNum">, + Desc<"The line at which to stop stepping - " + "defaults to the next line and only supported for step-in and " + "step-over. You can also pass the string 'block' to step to the " + "end of the current block. This is particularly useful in " + "conjunction with --step-target to step through a complex calling " + "sequence.">; def thread_step_scope_run_mode : Option<"run-mode", "m">, Group<1>, EnumArg<"RunMode">, Desc<"Determine how to run other " "threads while stepping the current thread.">; @@ -1149,9 +1189,13 @@ let Command = "thread jump" in { Completion<"SourceFile">, Desc<"Specifies the source file to jump to.">; def thread_jump_line : Option<"line", "l">, Group<1>, Arg<"LineNum">, Required, Desc<"Specifies the line number to jump to.">; - def thread_jump_by : Option<"by", "b">, Group<2>, Arg<"Offset">, Required, - Desc<"Jumps by a relative line offset from the current line," - "can be a positive or negative offset">; + def thread_jump_by + : Option<"by", "b">, + Group<2>, + Arg<"Offset">, + Required, + Desc<"Jumps by a relative line offset from the current line, " + "can be a positive or negative offset.">; def thread_jump_address : Option<"address", "a">, Group<3>, Arg<"AddressOrExpression">, Required, Desc<"Jumps to a specific address.">; def thread_jump_force : Option<"force", "r">, Groups<[1,2,3]>, @@ -1373,8 +1417,10 @@ let Command = "type category enable" in { } let Command = "type category disable" in { - def type_category_disable_language : Option<"language", "l">, Arg<"Language">, - Desc<"Enable the category for this language.">; + def type_category_disable_language + : Option<"language", "l">, + Arg<"Language">, + Desc<"Disable the category for this language.">; } let Command = "type filter add" in { diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index 925de2a..f2ed1f7 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -26,7 +26,11 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/ABI.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -41,6 +45,8 @@ #include "lldb/lldb-private-enumerations.h" #include "lldb/lldb-private-interfaces.h" #include "lldb/lldb-private-types.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" #include "llvm/TargetParser/Triple.h" @@ -280,6 +286,127 @@ bool Disassembler::ElideMixedSourceAndDisassemblyLine( return false; } +// For each instruction, this block attempts to resolve in-scope variables +// and determine if the current PC falls within their +// DWARF location entry. If so, it prints a simplified annotation using the +// variable name and its resolved location (e.g., "var = reg; " ). +// +// Annotations are only included if the variable has a valid DWARF location +// entry, and the location string is non-empty after filtering. Decoding +// errors and DWARF opcodes are intentionally omitted to keep the output +// concise and user-friendly. +// +// The goal is to give users helpful live variable hints alongside the +// disassembled instruction stream, similar to how debug information +// enhances source-level debugging. +std::vector<std::string> +VariableAnnotator::annotate(Instruction &inst, Target &target, + const lldb::ModuleSP &module_sp) { + std::vector<std::string> events; + + // If we lost module context, everything becomes <undef>. + if (!module_sp) { + for (const auto &KV : Live_) + events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); + Live_.clear(); + return events; + } + + // Resolve function/block at this *file* address. + SymbolContext sc; + const Address &iaddr = inst.GetAddress(); + const auto mask = eSymbolContextFunction | eSymbolContextBlock; + if (!module_sp->ResolveSymbolContextForAddress(iaddr, mask, sc) || + !sc.function) { + // No function context: everything dies here. + for (const auto &KV : Live_) + events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); + Live_.clear(); + return events; + } + + // Collect in-scope variables for this instruction into Current. + VariableList var_list; + // Innermost block containing iaddr. + if (Block *B = sc.block) { + auto filter = [](Variable *v) -> bool { return v && !v->IsArtificial(); }; + B->AppendVariables(/*can_create*/ true, + /*get_parent_variables*/ true, + /*stop_if_block_is_inlined_function*/ false, + /*filter*/ filter, + /*variable_list*/ &var_list); + } + + const lldb::addr_t pc_file = iaddr.GetFileAddress(); + const lldb::addr_t func_file = sc.function->GetAddress().GetFileAddress(); + + // ABI from Target (pretty reg names if plugin exists). Safe to be null. + lldb::ABISP abi_sp = ABI::FindPlugin(nullptr, target.GetArchitecture()); + ABI *abi = abi_sp.get(); + + llvm::DIDumpOptions opts; + opts.ShowAddresses = false; + // Prefer "register-only" output when we have an ABI. + opts.PrintRegisterOnly = static_cast<bool>(abi_sp); + + llvm::DenseMap<lldb::user_id_t, VarState> Current; + + for (size_t i = 0, e = var_list.GetSize(); i != e; ++i) { + lldb::VariableSP v = var_list.GetVariableAtIndex(i); + if (!v || v->IsArtificial()) + continue; + + const char *nm = v->GetName().AsCString(); + llvm::StringRef name = nm ? nm : "<anon>"; + + DWARFExpressionList &exprs = v->LocationExpressionList(); + if (!exprs.IsValid()) + continue; + + auto entry_or_err = exprs.GetExpressionEntryAtAddress(func_file, pc_file); + if (!entry_or_err) + continue; + + auto entry = *entry_or_err; + + StreamString loc_ss; + entry.expr->DumpLocation(&loc_ss, eDescriptionLevelBrief, abi, opts); + + llvm::StringRef loc = llvm::StringRef(loc_ss.GetString()).trim(); + if (loc.empty()) + continue; + + Current.try_emplace(v->GetID(), + VarState{std::string(name), std::string(loc)}); + } + + // Diff Live_ → Current. + + // 1) Starts/changes: iterate Current and compare with Live_. + for (const auto &KV : Current) { + auto it = Live_.find(KV.first); + if (it == Live_.end()) { + // Newly live. + events.emplace_back( + llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + } else if (it->second.last_loc != KV.second.last_loc) { + // Location changed. + events.emplace_back( + llvm::formatv("{0} = {1}", KV.second.name, KV.second.last_loc).str()); + } + } + + // 2) Ends: anything that was live but is not in Current becomes <undef>. + for (const auto &KV : Live_) { + if (!Current.count(KV.first)) + events.emplace_back(llvm::formatv("{0} = <undef>", KV.second.name).str()); + } + + // Commit new state. + Live_ = std::move(Current); + return events; +} + void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, const ExecutionContext &exe_ctx, bool mixed_source_and_assembly, @@ -376,6 +503,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, } } + VariableAnnotator annot; previous_symbol = nullptr; SourceLine previous_line; for (size_t i = 0; i < num_instructions_found; ++i) { @@ -540,10 +668,26 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, const bool show_bytes = (options & eOptionShowBytes) != 0; const bool show_control_flow_kind = (options & eOptionShowControlFlowKind) != 0; - inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, + + StreamString inst_line; + + inst->Dump(&inst_line, max_opcode_byte_size, true, show_bytes, show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr, address_text_size); + + if ((options & eOptionVariableAnnotations) && target_sp) { + auto annotations = annot.annotate(*inst, *target_sp, module_sp); + if (!annotations.empty()) { + const size_t annotation_column = 100; + inst_line.FillLastLineToColumn(annotation_column, ' '); + inst_line.PutCString("; "); + inst_line.PutCString(llvm::join(annotations, ", ")); + } + } + + strm.PutCString(inst_line.GetString()); strm.EOL(); + } else { break; } @@ -724,9 +868,7 @@ bool Instruction::DumpEmulation(const ArchSpec &arch) { return false; } -bool Instruction::CanSetBreakpoint () { - return !HasDelaySlot(); -} +bool Instruction::CanSetBreakpoint() { return !HasDelaySlot(); } bool Instruction::HasDelaySlot() { // Default is false. @@ -1016,6 +1158,16 @@ uint32_t InstructionList::GetMaxOpcocdeByteSize() const { return max_inst_size; } +size_t InstructionList::GetTotalByteSize() const { + size_t total_byte_size = 0; + collection::const_iterator pos, end; + for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end; + ++pos) { + total_byte_size += (*pos)->GetOpcode().GetByteSize(); + } + return total_byte_size; +} + InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const { InstructionSP inst_sp; if (idx < m_instructions.size()) @@ -1063,10 +1215,8 @@ void InstructionList::Append(lldb::InstructionSP &inst_sp) { m_instructions.push_back(inst_sp); } -uint32_t -InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, - bool ignore_calls, - bool *found_calls) const { +uint32_t InstructionList::GetIndexOfNextBranchInstruction( + uint32_t start, bool ignore_calls, bool *found_calls) const { size_t num_instructions = m_instructions.size(); uint32_t next_branch = UINT32_MAX; diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp index 4be9f3e..7580b15 100644 --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -211,7 +211,7 @@ ModuleSP DynamicLoader::LoadBinaryWithUUIDAndAddress( if (uuid.IsValid()) prog_str << uuid.GetAsString(); if (value_is_offset == 0 && value != LLDB_INVALID_ADDRESS) { - prog_str << "at 0x"; + prog_str << " at 0x"; prog_str.PutHex64(value); } diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index ce4db4e..91b9c00 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -556,3 +556,18 @@ void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const { break; } } + +ConstString Mangled::GetBaseName() const { + const auto &demangled_info = GetDemangledInfo(); + if (!demangled_info.has_value()) + return {}; + + ConstString demangled_name = GetDemangledName(); + if (!demangled_name) + return {}; + + const char *name_str = demangled_name.AsCString(); + const auto &range = demangled_info->BasenameRange; + return ConstString( + llvm::StringRef(name_str + range.first, range.second - range.first)); +} diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index d5ddc2b..bc63a41 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -755,6 +755,230 @@ size_t ModuleList::GetIndexForModule(const Module *module) const { } namespace { +/// A wrapper around ModuleList for shared modules. Provides fast lookups for +/// file-based ModuleSpec queries. +class SharedModuleList { +public: + /// Finds all the modules matching the module_spec, and adds them to \p + /// matching_module_list. + void FindModules(const ModuleSpec &module_spec, + ModuleList &matching_module_list) const { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + // Try map first for performance - if found, skip expensive full list + // search. + FindModulesInMap(module_spec, matching_module_list); + if (!matching_module_list.IsEmpty()) + return; + m_list.FindModules(module_spec, matching_module_list); + // Assert that modules were found in the list but not the map, it's + // because the module_spec has no filename or the found module has a + // different filename. For example, when searching by UUID and finding a + // module with an alias. + assert((matching_module_list.IsEmpty() || + module_spec.GetFileSpec().GetFilename().IsEmpty() || + module_spec.GetFileSpec().GetFilename() != + matching_module_list.GetModuleAtIndex(0) + ->GetFileSpec() + .GetFilename()) && + "Search by name not found in SharedModuleList's map"); + } + + ModuleSP FindModule(const Module &module) { + + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + if (ModuleSP result = FindModuleInMap(module)) + return result; + return m_list.FindModule(&module); + } + + // UUID searches bypass map since UUIDs aren't indexed by filename. + ModuleSP FindModule(const UUID &uuid) const { + return m_list.FindModule(uuid); + } + + void Append(const ModuleSP &module_sp, bool use_notifier) { + if (!module_sp) + return; + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + m_list.Append(module_sp, use_notifier); + AddToMap(module_sp); + } + + size_t RemoveOrphans(bool mandatory) { + std::unique_lock<std::recursive_mutex> lock(GetMutex(), std::defer_lock); + if (mandatory) { + lock.lock(); + } else { + if (!lock.try_lock()) + return 0; + } + size_t total_count = 0; + size_t run_count; + do { + // Remove indexed orphans first, then remove non-indexed orphans. This + // order is important because the shared count will be different if a + // module is indexed or not. + run_count = RemoveOrphansFromMapAndList(); + run_count += m_list.RemoveOrphans(mandatory); + total_count += run_count; + // Because removing orphans might make new orphans, remove from both + // containers until a fixed-point is reached. + } while (run_count != 0); + + return total_count; + } + + bool Remove(const ModuleSP &module_sp, bool use_notifier = true) { + if (!module_sp) + return false; + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + RemoveFromMap(*module_sp.get()); + return m_list.Remove(module_sp, use_notifier); + } + + void ReplaceEquivalent(const ModuleSP &module_sp, + llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + m_list.ReplaceEquivalent(module_sp, old_modules); + ReplaceEquivalentInMap(module_sp); + } + + bool RemoveIfOrphaned(const Module *module_ptr) { + std::lock_guard<std::recursive_mutex> guard(GetMutex()); + RemoveFromMap(*module_ptr, /*if_orphaned=*/true); + return m_list.RemoveIfOrphaned(module_ptr); + } + + std::recursive_mutex &GetMutex() const { return m_list.GetMutex(); } + +private: + ModuleSP FindModuleInMap(const Module &module) const { + if (!module.GetFileSpec().GetFilename()) + return ModuleSP(); + ConstString name = module.GetFileSpec().GetFilename(); + auto it = m_name_to_modules.find(name); + if (it == m_name_to_modules.end()) + return ModuleSP(); + const llvm::SmallVectorImpl<ModuleSP> &vector = it->second; + for (const ModuleSP &module_sp : vector) { + if (module_sp.get() == &module) + return module_sp; + } + return ModuleSP(); + } + + void FindModulesInMap(const ModuleSpec &module_spec, + ModuleList &matching_module_list) const { + auto it = m_name_to_modules.find(module_spec.GetFileSpec().GetFilename()); + if (it == m_name_to_modules.end()) + return; + const llvm::SmallVectorImpl<ModuleSP> &vector = it->second; + for (const ModuleSP &module_sp : vector) { + if (module_sp->MatchesModuleSpec(module_spec)) + matching_module_list.Append(module_sp); + } + } + + void AddToMap(const ModuleSP &module_sp) { + ConstString name = module_sp->GetFileSpec().GetFilename(); + if (name.IsEmpty()) + return; + m_name_to_modules[name].push_back(module_sp); + } + + void RemoveFromMap(const Module &module, bool if_orphaned = false) { + ConstString name = module.GetFileSpec().GetFilename(); + if (!m_name_to_modules.contains(name)) + return; + llvm::SmallVectorImpl<ModuleSP> &vec = m_name_to_modules[name]; + for (auto *it = vec.begin(); it != vec.end(); ++it) { + if (it->get() == &module) { + if (!if_orphaned || it->use_count() == kUseCountOrphaned) { + vec.erase(it); + break; + } + } + } + } + + void ReplaceEquivalentInMap(const ModuleSP &module_sp) { + RemoveEquivalentModulesFromMap(module_sp); + AddToMap(module_sp); + } + + void RemoveEquivalentModulesFromMap(const ModuleSP &module_sp) { + ConstString name = module_sp->GetFileSpec().GetFilename(); + if (name.IsEmpty()) + return; + + auto it = m_name_to_modules.find(name); + if (it == m_name_to_modules.end()) + return; + + // First remove any equivalent modules. Equivalent modules are modules + // whose path, platform path and architecture match. + ModuleSpec equivalent_module_spec(module_sp->GetFileSpec(), + module_sp->GetArchitecture()); + equivalent_module_spec.GetPlatformFileSpec() = + module_sp->GetPlatformFileSpec(); + + llvm::SmallVectorImpl<ModuleSP> &vec = it->second; + llvm::erase_if(vec, [&equivalent_module_spec](ModuleSP &element) { + return element->MatchesModuleSpec(equivalent_module_spec); + }); + } + + /// Remove orphans from the vector and return the removed modules. + ModuleList RemoveOrphansFromVector(llvm::SmallVectorImpl<ModuleSP> &vec) { + // remove_if moves the elements that match the condition to the end of the + // container, and returns an iterator to the first element that was moved. + auto *to_remove_start = llvm::remove_if(vec, [](const ModuleSP &module) { + return module.use_count() == kUseCountOrphaned; + }); + + ModuleList to_remove; + for (ModuleSP *it = to_remove_start; it != vec.end(); ++it) + to_remove.Append(*it); + + vec.erase(to_remove_start, vec.end()); + return to_remove; + } + + /// Remove orphans that exist in both the map and list. This does not remove + /// any orphans that exist exclusively on the list. + /// + /// The mutex must be locked by the caller. + int RemoveOrphansFromMapAndList() { + // Modules might hold shared pointers to other modules, so removing one + // module might orphan other modules. Keep removing modules until + // there are no further modules that can be removed. + int remove_count = 0; + int previous_remove_count; + do { + previous_remove_count = remove_count; + for (auto &[name, vec] : m_name_to_modules) { + if (vec.empty()) + continue; + ModuleList to_remove = RemoveOrphansFromVector(vec); + remove_count += to_remove.GetSize(); + m_list.Remove(to_remove); + } + // Break when fixed-point is reached. + } while (previous_remove_count != remove_count); + + return remove_count; + } + + ModuleList m_list; + + /// A hash map from a module's filename to all the modules that share that + /// filename, for fast module lookups by name. + llvm::DenseMap<ConstString, llvm::SmallVector<ModuleSP, 1>> m_name_to_modules; + + /// The use count of a module held only by m_list and m_name_to_modules. + static constexpr long kUseCountOrphaned = 2; +}; + struct SharedModuleListInfo { ModuleList module_list; ModuleListProperties module_list_properties; diff --git a/lldb/source/Core/ProtocolServer.cpp b/lldb/source/Core/ProtocolServer.cpp index 41636cd..38668f3 100644 --- a/lldb/source/Core/ProtocolServer.cpp +++ b/lldb/source/Core/ProtocolServer.cpp @@ -8,24 +8,29 @@ #include "lldb/Core/ProtocolServer.h" #include "lldb/Core/PluginManager.h" +#include "llvm/Support/Error.h" using namespace lldb_private; using namespace lldb; -ProtocolServer *ProtocolServer::GetOrCreate(llvm::StringRef name) { - static std::mutex g_mutex; +static std::pair<llvm::StringMap<ProtocolServerUP> &, std::mutex &> Servers() { static llvm::StringMap<ProtocolServerUP> g_protocol_server_instances; + static std::mutex g_mutex; + return {g_protocol_server_instances, g_mutex}; +} + +ProtocolServer *ProtocolServer::GetOrCreate(llvm::StringRef name) { + auto [protocol_server_instances, mutex] = Servers(); - std::lock_guard<std::mutex> guard(g_mutex); + std::lock_guard<std::mutex> guard(mutex); - auto it = g_protocol_server_instances.find(name); - if (it != g_protocol_server_instances.end()) + auto it = protocol_server_instances.find(name); + if (it != protocol_server_instances.end()) return it->second.get(); if (ProtocolServerCreateInstance create_callback = PluginManager::GetProtocolCreateCallbackForPluginName(name)) { - auto pair = - g_protocol_server_instances.try_emplace(name, create_callback()); + auto pair = protocol_server_instances.try_emplace(name, create_callback()); return pair.first->second.get(); } @@ -45,3 +50,18 @@ std::vector<llvm::StringRef> ProtocolServer::GetSupportedProtocols() { return supported_protocols; } + +llvm::Error ProtocolServer::Terminate() { + llvm::Error error = llvm::Error::success(); + + auto [protocol_server_instances, mutex] = Servers(); + std::lock_guard<std::mutex> guard(mutex); + for (auto &instance : protocol_server_instances) { + if (llvm::Error instance_error = instance.second->Stop()) + error = llvm::joinErrors(std::move(error), std::move(instance_error)); + } + + protocol_server_instances.clear(); + + return error; +} diff --git a/lldb/source/Core/Section.cpp b/lldb/source/Core/Section.cpp index 27dcf98..02d9d86 100644 --- a/lldb/source/Core/Section.cpp +++ b/lldb/source/Core/Section.cpp @@ -153,6 +153,8 @@ const char *Section::GetTypeAsCString() const { return "lldb-formatters"; case eSectionTypeSwiftModules: return "swift-modules"; + case eSectionTypeWasmName: + return "wasm-name"; case eSectionTypeOther: return "regular"; } @@ -415,6 +417,7 @@ bool Section::ContainsOnlyDebugInfo() const { case eSectionTypeCompactUnwind: case eSectionTypeGoSymtab: case eSectionTypeAbsoluteAddress: + case eSectionTypeWasmName: case eSectionTypeOther: // Used for "__dof_cache" in mach-o or ".debug" for COFF which isn't debug // information that we parse at all. This was causing system files with no diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp index 028f058..f9e65d3 100644 --- a/lldb/source/Core/Value.cpp +++ b/lldb/source/Core/Value.cpp @@ -347,6 +347,9 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data, else data.SetAddressByteSize(sizeof(void *)); + if (!type_size) + return Status::FromErrorString("type does not have a size"); + uint32_t result_byte_size = *type_size; if (m_value.GetData(data, result_byte_size)) return error; // Success; @@ -488,9 +491,11 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data, address = m_value.ULongLong(LLDB_INVALID_ADDRESS); address_type = eAddressTypeHost; if (exe_ctx) { - Target *target = exe_ctx->GetTargetPtr(); - if (target) { - data.SetByteOrder(target->GetArchitecture().GetByteOrder()); + if (Target *target = exe_ctx->GetTargetPtr()) { + // Registers are always stored in host endian. + data.SetByteOrder(m_context_type == ContextType::RegisterInfo + ? endian::InlHostByteOrder() + : target->GetArchitecture().GetByteOrder()); data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); break; } diff --git a/lldb/source/DataFormatters/DumpValueObjectOptions.cpp b/lldb/source/DataFormatters/DumpValueObjectOptions.cpp index c343e60..e1df952 100644 --- a/lldb/source/DataFormatters/DumpValueObjectOptions.cpp +++ b/lldb/source/DataFormatters/DumpValueObjectOptions.cpp @@ -17,7 +17,7 @@ DumpValueObjectOptions::DumpValueObjectOptions() : m_summary_sp(), m_root_valobj_name(), m_decl_printing_helper(), m_child_printing_decider(), m_pointer_as_array(), m_use_synthetic(true), m_scope_already_checked(false), m_flat_output(false), m_ignore_cap(false), - m_show_types(false), m_show_location(false), m_use_objc(false), + m_show_types(false), m_show_location(false), m_use_object_desc(false), m_hide_root_type(false), m_hide_root_name(false), m_hide_name(false), m_hide_value(false), m_run_validator(false), m_use_type_display_name(true), m_allow_oneliner_mode(true), @@ -65,8 +65,19 @@ DumpValueObjectOptions &DumpValueObjectOptions::SetShowLocation(bool show) { return *this; } -DumpValueObjectOptions &DumpValueObjectOptions::SetUseObjectiveC(bool use) { - m_use_objc = use; +DumpValueObjectOptions &DumpValueObjectOptions::DisableObjectDescription() { + // Reset these options to their default values. + SetUseObjectDescription(false); + SetHideRootType(false); + SetHideName(false); + SetHideValue(false); + SetShowSummary(true); + return *this; +} + +DumpValueObjectOptions & +DumpValueObjectOptions::SetUseObjectDescription(bool use) { + m_use_object_desc = use; return *this; } diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp index c2f8bb3..e200511 100644 --- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp +++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp @@ -14,9 +14,11 @@ #include "lldb/Target/Target.h" #include "lldb/Utility/Stream.h" #include "lldb/ValueObject/ValueObject.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MathExtras.h" #include <cstdint> #include <memory> +#include <optional> using namespace lldb; using namespace lldb_private; @@ -69,6 +71,18 @@ void ValueObjectPrinter::Init( SetupMostSpecializedValue(); } +static const char *maybeNewline(const std::string &s) { + // If the string already ends with a \n don't add another one. + if (s.empty() || s.back() != '\n') + return "\n"; + return ""; +} + +bool ValueObjectPrinter::ShouldPrintObjectDescription() { + return ShouldPrintValueObject() && m_options.m_use_object_desc && !IsNil() && + !IsUninitialized() && !m_options.m_pointer_as_array; +} + 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 @@ -77,6 +91,25 @@ llvm::Error ValueObjectPrinter::PrintValueObject() { !m_orig_valobj.GetCompilerType().IsValid()) return m_orig_valobj.GetError().ToError(); + std::optional<std::string> object_desc; + if (ShouldPrintObjectDescription()) { + // The object description is invoked now, but not printed until after + // value/summary. Calling GetObjectDescription at the outset of printing + // allows for early discovery of errors. In the case of an error, the value + // object is printed normally. + llvm::Expected<std::string> object_desc_or_err = + GetMostSpecializedValue().GetObjectDescription(); + if (!object_desc_or_err) { + auto error_msg = toString(object_desc_or_err.takeError()); + *m_stream << "error: " << error_msg << maybeNewline(error_msg); + + // Print the value object directly. + m_options.DisableObjectDescription(); + } else { + object_desc = *object_desc_or_err; + } + } + if (ShouldPrintValueObject()) { PrintLocationIfNeeded(); m_stream->Indent(); @@ -90,8 +123,10 @@ llvm::Error ValueObjectPrinter::PrintValueObject() { m_val_summary_ok = PrintValueAndSummaryIfNeeded(value_printed, summary_printed); - if (m_val_summary_ok) + if (m_val_summary_ok) { + PrintObjectDescriptionIfNeeded(object_desc); return PrintChildrenIfNeeded(value_printed, summary_printed); + } m_stream->EOL(); return llvm::Error::success(); @@ -144,24 +179,6 @@ void ValueObjectPrinter::SetupMostSpecializedValue() { "SetupMostSpecialized value must compute a valid ValueObject"); } -llvm::Expected<std::string> ValueObjectPrinter::GetDescriptionForDisplay() { - ValueObject &valobj = GetMostSpecializedValue(); - 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; -} - const char *ValueObjectPrinter::GetRootNameForDisplay() { const char *root_valobj_name = m_options.m_root_valobj_name.empty() @@ -468,38 +485,14 @@ bool ValueObjectPrinter::PrintValueAndSummaryIfNeeded(bool &value_printed, return !error_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 << ' '; - 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. - if (object_desc->empty() || object_desc->back() != '\n') - *m_stream << '\n'; - } - return llvm::Error::success(); - } - } - return llvm::Error::success(); +void ValueObjectPrinter::PrintObjectDescriptionIfNeeded( + std::optional<std::string> object_desc) { + if (!object_desc) + return; + + if (!m_options.m_hide_value || ShouldShowName()) + *m_stream << ' '; + *m_stream << *object_desc << maybeNewline(*object_desc); } bool DumpValueObjectOptions::PointerDepth::CanAllowExpansion() const { @@ -524,7 +517,7 @@ bool ValueObjectPrinter::ShouldPrintChildren( if (m_options.m_pointer_as_array) return true; - if (m_options.m_use_objc) + if (m_options.m_use_object_desc) return false; bool print_children = true; @@ -819,9 +812,6 @@ bool ValueObjectPrinter::PrintChildrenOneLiner(bool hide_names) { llvm::Error ValueObjectPrinter::PrintChildrenIfNeeded(bool value_printed, bool summary_printed) { - auto error = PrintObjectDescriptionIfNeeded(value_printed, summary_printed); - if (error) - return error; ValueObject &valobj = GetMostSpecializedValue(); diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index ed4e4e4..df56bcf 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -67,7 +67,8 @@ void DWARFExpression::UpdateValue(uint64_t const_value, } void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, - ABI *abi) const { + ABI *abi, + llvm::DIDumpOptions options) const { auto *MCRegInfo = abi ? &abi->GetMCRegisterInfo() : nullptr; auto GetRegName = [&MCRegInfo](uint64_t DwarfRegNum, bool IsEH) -> llvm::StringRef { @@ -79,10 +80,9 @@ void DWARFExpression::DumpLocation(Stream *s, lldb::DescriptionLevel level, return llvm::StringRef(RegName); return {}; }; - llvm::DIDumpOptions DumpOpts; - DumpOpts.GetNameForDWARFReg = GetRegName; + options.GetNameForDWARFReg = GetRegName; llvm::DWARFExpression E(m_data.GetAsLLVM(), m_data.GetAddressByteSize()); - llvm::printDwarfExpression(&E, s->AsRawOstream(), DumpOpts, nullptr); + llvm::printDwarfExpression(&E, s->AsRawOstream(), options, nullptr); } RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; } diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index e7a26d3..d557084 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -799,7 +799,7 @@ ResolveFunctionCallLabel(const FunctionCallLabel &label, auto sc_or_err = symbol_file->ResolveFunctionCallLabel(label); if (!sc_or_err) return llvm::joinErrors( - llvm::createStringError("failed to resolve function by UID"), + llvm::createStringError("failed to resolve function by UID:"), sc_or_err.takeError()); SymbolContextList sc_list; diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp index 329768d..771a9ab 100644 --- a/lldb/source/Expression/Materializer.cpp +++ b/lldb/source/Expression/Materializer.cpp @@ -1377,29 +1377,26 @@ public: return; } - DataExtractor register_data; - - if (!reg_value.GetData(register_data)) { - err = Status::FromErrorStringWithFormat( - "couldn't get the data for register %s", m_register_info.name); - return; - } - - if (register_data.GetByteSize() != m_register_info.byte_size) { + if (reg_value.GetByteSize() != m_register_info.byte_size) { err = Status::FromErrorStringWithFormat( "data for register %s had size %llu but we expected %llu", - m_register_info.name, (unsigned long long)register_data.GetByteSize(), + m_register_info.name, (unsigned long long)reg_value.GetByteSize(), (unsigned long long)m_register_info.byte_size); return; } - m_register_contents = std::make_shared<DataBufferHeap>( - register_data.GetDataStart(), register_data.GetByteSize()); + lldb_private::DataBufferHeap buf(reg_value.GetByteSize(), 0); + reg_value.GetAsMemoryData(m_register_info, buf.GetBytes(), + buf.GetByteSize(), map.GetByteOrder(), err); + if (!err.Success()) + return; + + m_register_contents = std::make_shared<DataBufferHeap>(buf); Status write_error; - map.WriteMemory(load_addr, register_data.GetDataStart(), - register_data.GetByteSize(), write_error); + map.WriteMemory(load_addr, buf.GetBytes(), reg_value.GetByteSize(), + write_error); if (!write_error.Success()) { err = Status::FromErrorStringWithFormat( diff --git a/lldb/source/Expression/REPL.cpp b/lldb/source/Expression/REPL.cpp index e5377d3..92017d2 100644 --- a/lldb/source/Expression/REPL.cpp +++ b/lldb/source/Expression/REPL.cpp @@ -321,7 +321,7 @@ void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) { const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors(); EvaluateExpressionOptions expr_options = m_expr_options; - expr_options.SetCoerceToId(m_varobj_options.use_objc); + expr_options.SetCoerceToId(m_varobj_options.use_object_desc); expr_options.SetKeepInMemory(true); expr_options.SetUseDynamic(m_varobj_options.use_dynamic); expr_options.SetGenerateDebugInfo(true); diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt index b15d72e..c9e8afe 100644 --- a/lldb/source/Host/CMakeLists.txt +++ b/lldb/source/Host/CMakeLists.txt @@ -7,7 +7,7 @@ if (APPLE AND LLVM_ENABLE_LOCAL_SUBMODULE_VISIBILITY) endif() endif() -if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") +if (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "AIX") add_definitions("-D_ALL_SOURCE") endif() diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index 5ed30fb..1fc86c8a 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -10,10 +10,8 @@ #include <iomanip> #include <optional> -#include "lldb/Host/ConnectionFileDescriptor.h" #include "lldb/Host/Editline.h" -#include "lldb/Host/FileSystem.h" -#include "lldb/Host/Host.h" +#include "lldb/Host/HostInfo.h" #include "lldb/Host/StreamFile.h" #include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/CompletionRequest.h" @@ -219,20 +217,19 @@ private: const char *GetHistoryFilePath() { // Compute the history path lazily. if (m_path.empty() && m_history && !m_prefix.empty()) { - llvm::SmallString<128> lldb_history_file; - FileSystem::Instance().GetHomeDirectory(lldb_history_file); - llvm::sys::path::append(lldb_history_file, ".lldb"); + FileSpec lldb_dir = HostInfo::GetUserLLDBDir(); // LLDB stores its history in ~/.lldb/. If for some reason this directory // isn't writable or cannot be created, history won't be available. - if (!llvm::sys::fs::create_directory(lldb_history_file)) { + if (!llvm::sys::fs::create_directory(lldb_dir.GetPath())) { #if LLDB_EDITLINE_USE_WCHAR std::string filename = m_prefix + "-widehistory"; #else std::string filename = m_prefix + "-history"; #endif - llvm::sys::path::append(lldb_history_file, filename); - m_path = std::string(lldb_history_file.str()); + FileSpec lldb_history_file = + lldb_dir.CopyByAppendingPathComponent(filename); + m_path = lldb_history_file.GetPath(); } } diff --git a/lldb/source/Host/common/HostInfoBase.cpp b/lldb/source/Host/common/HostInfoBase.cpp index 89dfe4a..a02ac77 100644 --- a/lldb/source/Host/common/HostInfoBase.cpp +++ b/lldb/source/Host/common/HostInfoBase.cpp @@ -61,6 +61,10 @@ struct HostInfoBaseFields { FileSpec m_lldb_clang_resource_dir; llvm::once_flag m_lldb_system_plugin_dir_once; FileSpec m_lldb_system_plugin_dir; + llvm::once_flag m_lldb_user_home_dir_once; + FileSpec m_lldb_user_home_dir; + llvm::once_flag m_lldb_user_lldb_dir_once; + FileSpec m_lldb_user_lldb_dir; llvm::once_flag m_lldb_user_plugin_dir_once; FileSpec m_lldb_user_plugin_dir; llvm::once_flag m_lldb_process_tmp_dir_once; @@ -161,6 +165,26 @@ FileSpec HostInfoBase::GetSystemPluginDir() { return g_fields->m_lldb_system_plugin_dir; } +FileSpec HostInfoBase::GetUserHomeDir() { + llvm::call_once(g_fields->m_lldb_user_home_dir_once, []() { + if (!HostInfo::ComputeUserHomeDirectory(g_fields->m_lldb_user_home_dir)) + g_fields->m_lldb_user_home_dir = FileSpec(); + LLDB_LOG(GetLog(LLDBLog::Host), "user home dir -> `{0}`", + g_fields->m_lldb_user_home_dir); + }); + return g_fields->m_lldb_user_home_dir; +} + +FileSpec HostInfoBase::GetUserLLDBDir() { + llvm::call_once(g_fields->m_lldb_user_lldb_dir_once, []() { + if (!HostInfo::ComputeUserLLDBHomeDirectory(g_fields->m_lldb_user_lldb_dir)) + g_fields->m_lldb_user_lldb_dir = FileSpec(); + LLDB_LOG(GetLog(LLDBLog::Host), "user lldb home dir -> `{0}`", + g_fields->m_lldb_user_lldb_dir); + }); + return g_fields->m_lldb_user_lldb_dir; +} + FileSpec HostInfoBase::GetUserPluginDir() { llvm::call_once(g_fields->m_lldb_user_plugin_dir_once, []() { if (!HostInfo::ComputeUserPluginsDirectory( @@ -316,6 +340,20 @@ bool HostInfoBase::ComputeSystemPluginsDirectory(FileSpec &file_spec) { return false; } +bool HostInfoBase::ComputeUserHomeDirectory(FileSpec &file_spec) { + FileSpec temp_file("~"); + FileSystem::Instance().Resolve(temp_file); + file_spec.SetDirectory(temp_file.GetPathAsConstString()); + return true; +} + +bool HostInfoBase::ComputeUserLLDBHomeDirectory(FileSpec &file_spec) { + FileSpec home_dir_spec = GetUserHomeDir(); + home_dir_spec.AppendPathComponent(".lldb"); + file_spec.SetDirectory(home_dir_spec.GetPathAsConstString()); + return true; +} + bool HostInfoBase::ComputeUserPluginsDirectory(FileSpec &file_spec) { // TODO(zturner): Figure out how to compute the user plugins directory for // all platforms. diff --git a/lldb/source/Host/common/JSONTransport.cpp b/lldb/source/Host/common/JSONTransport.cpp index 546c12c..c4b42ea 100644 --- a/lldb/source/Host/common/JSONTransport.cpp +++ b/lldb/source/Host/common/JSONTransport.cpp @@ -7,171 +7,26 @@ //===----------------------------------------------------------------------===// #include "lldb/Host/JSONTransport.h" -#include "lldb/Utility/IOObject.h" -#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" -#include "lldb/Utility/SelectHelper.h" #include "lldb/Utility/Status.h" -#include "lldb/lldb-forward.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" -#include <optional> #include <string> -#include <utility> using namespace llvm; using namespace lldb; using namespace lldb_private; -/// ReadFull attempts to read the specified number of bytes. If EOF is -/// encountered, an empty string is returned. -static Expected<std::string> -ReadFull(IOObject &descriptor, size_t length, - std::optional<std::chrono::microseconds> timeout = std::nullopt) { - if (!descriptor.IsValid()) - return llvm::make_error<TransportInvalidError>(); +char TransportUnhandledContentsError::ID; - bool timeout_supported = true; - // FIXME: SelectHelper does not work with NativeFile on Win32. -#if _WIN32 - timeout_supported = descriptor.GetFdType() == IOObject::eFDTypeSocket; -#endif +TransportUnhandledContentsError::TransportUnhandledContentsError( + std::string unhandled_contents) + : m_unhandled_contents(unhandled_contents) {} - if (timeout && timeout_supported) { - SelectHelper sh; - sh.SetTimeout(*timeout); - sh.FDSetRead( - reinterpret_cast<lldb::socket_t>(descriptor.GetWaitableHandle())); - Status status = sh.Select(); - if (status.Fail()) { - // Convert timeouts into a specific error. - if (status.GetType() == lldb::eErrorTypePOSIX && - status.GetError() == ETIMEDOUT) - return make_error<TransportTimeoutError>(); - return status.takeError(); - } - } - - std::string data; - data.resize(length); - Status status = descriptor.Read(data.data(), length); - if (status.Fail()) - return status.takeError(); - - // Read returns '' on EOF. - if (length == 0) - return make_error<TransportEOFError>(); - - // Return the actual number of bytes read. - return data.substr(0, length); -} - -static Expected<std::string> -ReadUntil(IOObject &descriptor, StringRef delimiter, - std::optional<std::chrono::microseconds> timeout = std::nullopt) { - std::string buffer; - buffer.reserve(delimiter.size() + 1); - while (!llvm::StringRef(buffer).ends_with(delimiter)) { - Expected<std::string> next = - ReadFull(descriptor, buffer.empty() ? delimiter.size() : 1, timeout); - if (auto Err = next.takeError()) - return std::move(Err); - buffer += *next; - } - return buffer.substr(0, buffer.size() - delimiter.size()); -} - -JSONTransport::JSONTransport(IOObjectSP input, IOObjectSP output) - : m_input(std::move(input)), m_output(std::move(output)) {} - -void JSONTransport::Log(llvm::StringRef message) { - LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message); +void TransportUnhandledContentsError::log(llvm::raw_ostream &OS) const { + OS << "transport EOF with unhandled contents: '" << m_unhandled_contents + << "'"; } - -Expected<std::string> -HTTPDelimitedJSONTransport::ReadImpl(const std::chrono::microseconds &timeout) { - if (!m_input || !m_input->IsValid()) - return llvm::make_error<TransportInvalidError>(); - - IOObject *input = m_input.get(); - Expected<std::string> message_header = - ReadFull(*input, kHeaderContentLength.size(), timeout); - if (!message_header) - return message_header.takeError(); - if (*message_header != kHeaderContentLength) - return createStringError(formatv("expected '{0}' and got '{1}'", - kHeaderContentLength, *message_header) - .str()); - - Expected<std::string> raw_length = ReadUntil(*input, kHeaderSeparator); - if (!raw_length) - return handleErrors(raw_length.takeError(), - [&](const TransportEOFError &E) -> llvm::Error { - return createStringError( - "unexpected EOF while reading header separator"); - }); - - size_t length; - if (!to_integer(*raw_length, length)) - return createStringError( - formatv("invalid content length {0}", *raw_length).str()); - - Expected<std::string> raw_json = ReadFull(*input, length); - if (!raw_json) - return handleErrors( - raw_json.takeError(), [&](const TransportEOFError &E) -> llvm::Error { - return createStringError("unexpected EOF while reading JSON"); - }); - - Log(llvm::formatv("--> {0}", *raw_json).str()); - - return raw_json; +std::error_code TransportUnhandledContentsError::convertToErrorCode() const { + return std::make_error_code(std::errc::bad_message); } - -Error HTTPDelimitedJSONTransport::WriteImpl(const std::string &message) { - if (!m_output || !m_output->IsValid()) - return llvm::make_error<TransportInvalidError>(); - - Log(llvm::formatv("<-- {0}", message).str()); - - std::string Output; - raw_string_ostream OS(Output); - OS << kHeaderContentLength << message.length() << kHeaderSeparator << message; - size_t num_bytes = Output.size(); - return m_output->Write(Output.data(), num_bytes).takeError(); -} - -Expected<std::string> -JSONRPCTransport::ReadImpl(const std::chrono::microseconds &timeout) { - if (!m_input || !m_input->IsValid()) - return make_error<TransportInvalidError>(); - - IOObject *input = m_input.get(); - Expected<std::string> raw_json = - ReadUntil(*input, kMessageSeparator, timeout); - if (!raw_json) - return raw_json.takeError(); - - Log(llvm::formatv("--> {0}", *raw_json).str()); - - return *raw_json; -} - -Error JSONRPCTransport::WriteImpl(const std::string &message) { - if (!m_output || !m_output->IsValid()) - return llvm::make_error<TransportInvalidError>(); - - Log(llvm::formatv("<-- {0}", message).str()); - - std::string Output; - llvm::raw_string_ostream OS(Output); - OS << message << kMessageSeparator; - size_t num_bytes = Output.size(); - return m_output->Write(Output.data(), num_bytes).takeError(); -} - -char TransportEOFError::ID; -char TransportTimeoutError::ID; -char TransportInvalidError::ID; diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm index 61f9419..79e1322 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -39,7 +39,7 @@ #include <Foundation/Foundation.h> #include <mach-o/dyld.h> #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 + MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 #if __has_include(<mach-o/dyld_introspection.h>) #include <mach-o/dyld_introspection.h> #define SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS @@ -78,8 +78,8 @@ std::optional<std::string> HostInfoMacOSX::GetOSBuildString() { static void ParseOSVersion(llvm::VersionTuple &version, NSString *Key) { @autoreleasepool { NSDictionary *version_info = - [NSDictionary dictionaryWithContentsOfFile: - @"/System/Library/CoreServices/SystemVersion.plist"]; + [NSDictionary dictionaryWithContentsOfFile: + @"/System/Library/CoreServices/SystemVersion.plist"]; NSString *version_value = [version_info objectForKey: Key]; const char *version_str = [version_value UTF8String]; version.tryParse(version_str); @@ -225,9 +225,9 @@ bool HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) { } bool HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) { - FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns"); - FileSystem::Instance().Resolve(temp_file); - file_spec.SetDirectory(temp_file.GetPathAsConstString()); + FileSpec home_dir_spec = GetUserHomeDir(); + home_dir_spec.AppendPathComponent("Library/Application Support/LLDB/PlugIns"); + file_spec.SetDirectory(home_dir_spec.GetPathAsConstString()); return true; } diff --git a/lldb/source/Host/windows/Host.cpp b/lldb/source/Host/windows/Host.cpp index 4e747f7..4277b8e 100644 --- a/lldb/source/Host/windows/Host.cpp +++ b/lldb/source/Host/windows/Host.cpp @@ -22,6 +22,7 @@ #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StructuredData.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ManagedStatic.h" @@ -32,6 +33,8 @@ using namespace lldb; using namespace lldb_private; +using llvm::sys::windows::UTF8ToUTF16; + static bool GetTripleForProcess(const FileSpec &executable, llvm::Triple &triple) { // Open the PE File as a binary file, and parse just enough information to @@ -325,31 +328,18 @@ private: static llvm::ManagedStatic<WindowsEventLog> event_log; -static std::wstring AnsiToUtf16(const std::string &ansi) { - if (ansi.empty()) - return {}; - - const int unicode_length = - MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, nullptr, 0); - if (unicode_length == 0) - return {}; - - std::wstring unicode(unicode_length, L'\0'); - MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, &unicode[0], unicode_length); - return unicode; -} - void Host::SystemLog(Severity severity, llvm::StringRef message) { + if (message.empty()) + return; + HANDLE h = event_log->GetHandle(); if (!h) return; - std::wstring wide_message = AnsiToUtf16(message.str()); - if (wide_message.empty()) + llvm::SmallVector<wchar_t, 1> argsUTF16; + if (UTF8ToUTF16(message.str(), argsUTF16)) return; - LPCWSTR msg_ptr = wide_message.c_str(); - WORD event_type; switch (severity) { case lldb::eSeverityWarning: @@ -363,5 +353,7 @@ void Host::SystemLog(Severity severity, llvm::StringRef message) { event_type = EVENTLOG_INFORMATION_TYPE; } - ReportEventW(h, event_type, 0, 0, nullptr, 1, 0, &msg_ptr, nullptr); + LPCWSTR messages[1] = {argsUTF16.data()}; + ReportEventW(h, event_type, 0, 0, nullptr, std::size(messages), 0, messages, + nullptr); } diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index a0080cf..d06e8c3 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -52,6 +52,7 @@ #include "lldb/Core/Telemetry.h" #include "lldb/Host/StreamFile.h" #include "lldb/Utility/ErrorMessages.h" +#include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/State.h" @@ -335,7 +336,7 @@ void CommandInterpreter::Initialize() { AddAlias("ni", cmd_obj_sp); } - cmd_obj_sp = GetCommandSPExact("thread step-in"); + cmd_obj_sp = GetCommandSPExact("_regexp-step"); if (cmd_obj_sp) { AddAlias("s", cmd_obj_sp); AddAlias("step", cmd_obj_sp); @@ -946,6 +947,27 @@ void CommandInterpreter::LoadCommandDictionary() { jump_regex_cmd_sp; } } + + std::shared_ptr<CommandObjectRegexCommand> step_regex_cmd_sp( + new CommandObjectRegexCommand( + *this, "_regexp-step", + "Single step, optionally to a specific function.", + "\n" + "_regexp-step // Single step\n" + "_regexp-step <function-name> // Step into the named function\n", + 0, false)); + if (step_regex_cmd_sp) { + if (step_regex_cmd_sp->AddRegexCommand("^[[:space:]]*$", + "thread step-in") && + step_regex_cmd_sp->AddRegexCommand("^[[:space:]]*(-.+)$", + "thread step-in %1") && + step_regex_cmd_sp->AddRegexCommand( + "^[[:space:]]*(.+)[[:space:]]*$", + "thread step-in --end-linenumber block --step-in-target %1")) { + m_command_dict[std::string(step_regex_cmd_sp->GetCommandName())] = + step_regex_cmd_sp; + } + } } int CommandInterpreter::GetCommandNamesMatchingPartialString( @@ -2481,22 +2503,18 @@ int CommandInterpreter::GetOptionArgumentPosition(const char *in_string) { return position; } -static void GetHomeInitFile(llvm::SmallVectorImpl<char> &init_file, - llvm::StringRef suffix = {}) { +static void GetHomeInitFile(FileSpec &init_file, llvm::StringRef suffix = {}) { std::string init_file_name = ".lldbinit"; if (!suffix.empty()) { init_file_name.append("-"); init_file_name.append(suffix.str()); } - FileSystem::Instance().GetHomeDirectory(init_file); - llvm::sys::path::append(init_file, init_file_name); - - FileSystem::Instance().Resolve(init_file); + init_file = + HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name); } -static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file, - LanguageType language) { +static void GetHomeREPLInitFile(FileSpec &init_file, LanguageType language) { if (language == eLanguageTypeUnknown) { LanguageSet repl_languages = Language::GetLanguagesSupportingREPLs(); if (auto main_repl_language = repl_languages.GetSingularLanguage()) @@ -2510,9 +2528,9 @@ static void GetHomeREPLInitFile(llvm::SmallVectorImpl<char> &init_file, llvm::Twine(Language::GetNameForLanguageType(language)) + llvm::Twine("-repl")) .str(); - FileSystem::Instance().GetHomeDirectory(init_file); - llvm::sys::path::append(init_file, init_file_name); - FileSystem::Instance().Resolve(init_file); + + init_file = + HostInfo::GetUserHomeDir().CopyByAppendingPathComponent(init_file_name); } static void GetCwdInitFile(llvm::SmallVectorImpl<char> &init_file) { @@ -2567,10 +2585,10 @@ void CommandInterpreter::SourceInitFileCwd(CommandReturnObject &result) { SourceInitFile(FileSpec(init_file.str()), result); break; case eLoadCWDlldbinitWarn: { - llvm::SmallString<128> home_init_file; + FileSpec home_init_file; GetHomeInitFile(home_init_file); if (llvm::sys::path::parent_path(init_file) == - llvm::sys::path::parent_path(home_init_file)) { + llvm::sys::path::parent_path(home_init_file.GetPath())) { result.SetStatus(eReturnStatusSuccessFinishNoResult); } else { result.AppendError(InitFileWarning); @@ -2590,24 +2608,24 @@ void CommandInterpreter::SourceInitFileHome(CommandReturnObject &result, return; } - llvm::SmallString<128> init_file; + FileSpec init_file; if (is_repl) GetHomeREPLInitFile(init_file, GetDebugger().GetREPLLanguage()); - if (init_file.empty()) + if (init_file.GetPath().empty()) GetHomeInitFile(init_file); if (!m_skip_app_init_files) { llvm::StringRef program_name = HostInfo::GetProgramFileSpec().GetFilename().GetStringRef(); - llvm::SmallString<128> program_init_file; + FileSpec program_init_file; GetHomeInitFile(program_init_file, program_name); if (FileSystem::Instance().Exists(program_init_file)) init_file = program_init_file; } - SourceInitFile(FileSpec(init_file.str()), result); + SourceInitFile(init_file, result); } void CommandInterpreter::SourceInitFileGlobal(CommandReturnObject &result) { diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 129646e..43e19b3 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -359,7 +359,8 @@ bool CommandObject::HelpTextContainsWord(llvm::StringRef search_word, StreamString usage_help; GetOptions()->GenerateOptionUsage( usage_help, *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); if (!usage_help.Empty()) { llvm::StringRef usage_text = usage_help.GetString(); if (usage_text.contains_insensitive(search_word)) @@ -672,7 +673,8 @@ void CommandObject::GenerateHelpText(Stream &output_strm) { if (options != nullptr) { options->GenerateOptionUsage( output_strm, *this, - GetCommandInterpreter().GetDebugger().GetTerminalWidth()); + GetCommandInterpreter().GetDebugger().GetTerminalWidth(), + GetCommandInterpreter().GetDebugger().GetUseColor()); } llvm::StringRef long_help = GetHelpLong(); if (!long_help.empty()) { diff --git a/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp index d633c46..1939d79 100644 --- a/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp +++ b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp @@ -90,7 +90,7 @@ Status OptionGroupValueObjectDisplay::SetOptionValue( flat_output = true; break; case 'O': - use_objc = true; + use_object_desc = true; break; case 'R': be_raw = true; @@ -163,7 +163,7 @@ void OptionGroupValueObjectDisplay::OptionParsingStarting( no_summary_depth = 0; show_location = false; flat_output = false; - use_objc = false; + use_object_desc = false; max_depth = UINT32_MAX; max_depth_is_default = true; ptr_depth = 0; @@ -191,14 +191,14 @@ DumpValueObjectOptions OptionGroupValueObjectDisplay::GetAsDumpOptions( lldb::Format format, lldb::TypeSummaryImplSP summary_sp) { DumpValueObjectOptions options; options.SetMaximumPointerDepth(ptr_depth); - if (use_objc) + if (use_object_desc) options.SetShowSummary(false); else options.SetOmitSummaryDepth(no_summary_depth); options.SetMaximumDepth(max_depth, max_depth_is_default) .SetShowTypes(show_types) .SetShowLocation(show_location) - .SetUseObjectiveC(use_objc) + .SetUseObjectDescription(use_object_desc) .SetUseDynamicType(use_dynamic) .SetUseSyntheticValue(use_synth) .SetFlatOutput(flat_output) @@ -208,8 +208,9 @@ DumpValueObjectOptions OptionGroupValueObjectDisplay::GetAsDumpOptions( if (lang_descr_verbosity == eLanguageRuntimeDescriptionDisplayVerbosityCompact) - options.SetHideRootType(use_objc).SetHideName(use_objc).SetHideValue( - use_objc); + options.SetHideRootType(use_object_desc) + .SetHideName(use_object_desc) + .SetHideValue(use_object_desc); if (be_raw) options.SetRawDisplay(); diff --git a/lldb/source/Interpreter/OptionValueArch.cpp b/lldb/source/Interpreter/OptionValueArch.cpp index ea15cca..a960e39 100644 --- a/lldb/source/Interpreter/OptionValueArch.cpp +++ b/lldb/source/Interpreter/OptionValueArch.cpp @@ -11,6 +11,7 @@ #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/State.h" @@ -30,6 +31,12 @@ void OptionValueArch::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (arch_name) strm.PutCString(arch_name); } + + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && m_default_value.IsValid()) { + DefaultValueFormat label(strm); + strm.PutCString(m_default_value.GetArchitectureName()); + } } } diff --git a/lldb/source/Interpreter/OptionValueArray.cpp b/lldb/source/Interpreter/OptionValueArray.cpp index f6c14de..5600333 100644 --- a/lldb/source/Interpreter/OptionValueArray.cpp +++ b/lldb/source/Interpreter/OptionValueArray.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" @@ -27,8 +28,15 @@ void OptionValueArray::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionValue) { const bool one_line = dump_mask & eDumpOptionCommand; const uint32_t size = m_values.size(); - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", (m_values.size() > 0 && !one_line) ? "\n" : ""); + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { + strm.PutCString(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_values.empty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + if (!m_values.empty() && !one_line) + strm.PutCString("\n"); + } if (!one_line) strm.IndentMore(); for (uint32_t i = 0; i < size; ++i) { diff --git a/lldb/source/Interpreter/OptionValueBoolean.cpp b/lldb/source/Interpreter/OptionValueBoolean.cpp index d4fda76..023c243 100644 --- a/lldb/source/Interpreter/OptionValueBoolean.cpp +++ b/lldb/source/Interpreter/OptionValueBoolean.cpp @@ -10,6 +10,7 @@ #include "lldb/Host/PosixApi.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #include "llvm/ADT/STLExtras.h" @@ -27,6 +28,11 @@ void OptionValueBoolean::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.PutCString(m_current_value ? "true" : "false"); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.PutCString(m_default_value ? "true" : "false"); + } } } diff --git a/lldb/source/Interpreter/OptionValueChar.cpp b/lldb/source/Interpreter/OptionValueChar.cpp index 2aadcff..595dcba 100644 --- a/lldb/source/Interpreter/OptionValueChar.cpp +++ b/lldb/source/Interpreter/OptionValueChar.cpp @@ -9,6 +9,7 @@ #include "lldb/Interpreter/OptionValueChar.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" #include "llvm/ADT/STLExtras.h" @@ -16,6 +17,13 @@ using namespace lldb; using namespace lldb_private; +static void DumpChar(Stream &strm, char value) { + if (value != '\0') + strm.PutChar(value); + else + strm.PutCString("(null)"); +} + void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) @@ -24,10 +32,12 @@ void OptionValueChar::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - if (m_current_value != '\0') - strm.PutChar(m_current_value); - else - strm.PutCString("(null)"); + DumpChar(strm, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + DumpChar(strm, m_default_value); + } } } diff --git a/lldb/source/Interpreter/OptionValueDictionary.cpp b/lldb/source/Interpreter/OptionValueDictionary.cpp index 19e21dd..0efc76b 100644 --- a/lldb/source/Interpreter/OptionValueDictionary.cpp +++ b/lldb/source/Interpreter/OptionValueDictionary.cpp @@ -9,6 +9,7 @@ #include "lldb/Interpreter/OptionValueDictionary.h" #include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Interpreter/OptionValueEnumeration.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Utility/Args.h" @@ -30,8 +31,13 @@ void OptionValueDictionary::DumpValue(const ExecutionContext *exe_ctx, } if (dump_mask & eDumpOptionValue) { const bool one_line = dump_mask & eDumpOptionCommand; - if (dump_mask & eDumpOptionType) + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { strm.PutCString(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_values.empty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + } if (!one_line) strm.IndentMore(); diff --git a/lldb/source/Interpreter/OptionValueEnumeration.cpp b/lldb/source/Interpreter/OptionValueEnumeration.cpp index cf64623..eb31bde 100644 --- a/lldb/source/Interpreter/OptionValueEnumeration.cpp +++ b/lldb/source/Interpreter/OptionValueEnumeration.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/StringList.h" using namespace lldb; @@ -19,6 +20,17 @@ OptionValueEnumeration::OptionValueEnumeration( SetEnumerations(enumerators); } +void OptionValueEnumeration::DumpEnum(Stream &strm, enum_type value) { + const size_t count = m_enumerations.GetSize(); + for (size_t i = 0; i < count; ++i) + if (m_enumerations.GetValueAtIndexUnchecked(i).value == value) { + strm.PutCString(m_enumerations.GetCStringAtIndex(i)); + return; + } + + strm.Printf("%" PRIu64, (uint64_t)value); +} + void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) @@ -26,14 +38,12 @@ void OptionValueEnumeration::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - const size_t count = m_enumerations.GetSize(); - for (size_t i = 0; i < count; ++i) { - if (m_enumerations.GetValueAtIndexUnchecked(i).value == m_current_value) { - strm.PutCString(m_enumerations.GetCStringAtIndex(i).GetStringRef()); - return; - } + DumpEnum(strm, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + DumpEnum(strm, m_default_value); } - strm.Printf("%" PRIu64, (uint64_t)m_current_value); } } diff --git a/lldb/source/Interpreter/OptionValueFileSpec.cpp b/lldb/source/Interpreter/OptionValueFileSpec.cpp index 6fa6af4..8d4966d 100644 --- a/lldb/source/Interpreter/OptionValueFileSpec.cpp +++ b/lldb/source/Interpreter/OptionValueFileSpec.cpp @@ -12,6 +12,7 @@ #include "lldb/Host/FileSystem.h" #include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/State.h" @@ -41,7 +42,12 @@ void OptionValueFileSpec::DumpValue(const ExecutionContext *exe_ctx, strm.PutCString(" = "); if (m_current_value) { - strm << '"' << m_current_value.GetPath().c_str() << '"'; + strm << '"' << m_current_value.GetPath() << '"'; + } + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && m_default_value) { + DefaultValueFormat label(strm); + strm << '"' << m_default_value.GetPath() << '"'; } } } diff --git a/lldb/source/Interpreter/OptionValueFileSpecList.cpp b/lldb/source/Interpreter/OptionValueFileSpecList.cpp index f252dc4..f331c5d 100644 --- a/lldb/source/Interpreter/OptionValueFileSpecList.cpp +++ b/lldb/source/Interpreter/OptionValueFileSpecList.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" @@ -22,9 +23,15 @@ void OptionValueFileSpecList::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionValue) { const bool one_line = dump_mask & eDumpOptionCommand; const uint32_t size = m_current_value.GetSize(); - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", - (m_current_value.GetSize() > 0 && !one_line) ? "\n" : ""); + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { + strm.Printf(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_current_value.IsEmpty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + if (!m_current_value.IsEmpty() && !one_line) + strm.PutCString("\n"); + } if (!one_line) strm.IndentMore(); for (uint32_t i = 0; i < size; ++i) { diff --git a/lldb/source/Interpreter/OptionValueFormat.cpp b/lldb/source/Interpreter/OptionValueFormat.cpp index bc4e779..05990fb 100644 --- a/lldb/source/Interpreter/OptionValueFormat.cpp +++ b/lldb/source/Interpreter/OptionValueFormat.cpp @@ -10,6 +10,7 @@ #include "lldb/DataFormatters/FormatManager.h" #include "lldb/Interpreter/OptionArgParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -23,6 +24,11 @@ void OptionValueFormat::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.PutCString(FormatManager::GetFormatAsCString(m_current_value)); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.PutCString(FormatManager::GetFormatAsCString(m_default_value)); + } } } diff --git a/lldb/source/Interpreter/OptionValueFormatEntity.cpp b/lldb/source/Interpreter/OptionValueFormatEntity.cpp index d8b8301..b31dd4e 100644 --- a/lldb/source/Interpreter/OptionValueFormatEntity.cpp +++ b/lldb/source/Interpreter/OptionValueFormatEntity.cpp @@ -10,6 +10,7 @@ #include "lldb/Core/Module.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/StringList.h" using namespace lldb; @@ -33,10 +34,9 @@ void OptionValueFormatEntity::Clear() { m_value_was_set = false; } -static void EscapeBackticks(llvm::StringRef str, std::string &dst) { - dst.clear(); +static std::string EscapeBackticks(llvm::StringRef str) { + std::string dst; dst.reserve(str.size()); - for (size_t i = 0, e = str.size(); i != e; ++i) { char c = str[i]; if (c == '`') { @@ -45,6 +45,7 @@ static void EscapeBackticks(llvm::StringRef str, std::string &dst) { } dst += c; } + return dst; } void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx, @@ -54,17 +55,18 @@ void OptionValueFormatEntity::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - std::string escaped; - EscapeBackticks(m_current_format, escaped); - strm << '"' << escaped << '"'; + strm << '"' << EscapeBackticks(m_current_format) << '"'; + if (dump_mask & eDumpOptionDefaultValue && + m_current_format != m_default_format) { + DefaultValueFormat label(strm); + strm << '"' << EscapeBackticks(m_default_format) << '"'; + } } } llvm::json::Value OptionValueFormatEntity::ToJSON(const ExecutionContext *exe_ctx) const { - std::string escaped; - EscapeBackticks(m_current_format, escaped); - return escaped; + return EscapeBackticks(m_current_format); } Status OptionValueFormatEntity::SetValueFromString(llvm::StringRef value_str, diff --git a/lldb/source/Interpreter/OptionValueLanguage.cpp b/lldb/source/Interpreter/OptionValueLanguage.cpp index 0fdaacb..bb28dc8 100644 --- a/lldb/source/Interpreter/OptionValueLanguage.cpp +++ b/lldb/source/Interpreter/OptionValueLanguage.cpp @@ -9,8 +9,9 @@ #include "lldb/Interpreter/OptionValueLanguage.h" #include "lldb/DataFormatters/FormatManager.h" -#include "lldb/Target/Language.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Symbol/TypeSystem.h" +#include "lldb/Target/Language.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" @@ -26,6 +27,12 @@ void OptionValueLanguage::DumpValue(const ExecutionContext *exe_ctx, strm.PutCString(" = "); if (m_current_value != eLanguageTypeUnknown) strm.PutCString(Language::GetNameForLanguageType(m_current_value)); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && + m_default_value != eLanguageTypeUnknown) { + DefaultValueFormat label(strm); + strm.PutCString(Language::GetNameForLanguageType(m_default_value)); + } } } diff --git a/lldb/source/Interpreter/OptionValuePathMappings.cpp b/lldb/source/Interpreter/OptionValuePathMappings.cpp index 95b8e64..abf4d42 100644 --- a/lldb/source/Interpreter/OptionValuePathMappings.cpp +++ b/lldb/source/Interpreter/OptionValuePathMappings.cpp @@ -9,6 +9,7 @@ #include "lldb/Interpreter/OptionValuePathMappings.h" #include "lldb/Host/FileSystem.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Stream.h" @@ -28,8 +29,15 @@ void OptionValuePathMappings::DumpValue(const ExecutionContext *exe_ctx, if (dump_mask & eDumpOptionType) strm.Printf("(%s)", GetTypeAsCString()); if (dump_mask & eDumpOptionValue) { - if (dump_mask & eDumpOptionType) - strm.Printf(" =%s", (m_path_mappings.GetSize() > 0) ? "\n" : ""); + if (dump_mask & (eDumpOptionType | eDumpOptionDefaultValue)) { + strm.Printf(" ="); + if (dump_mask & eDumpOptionDefaultValue && !m_path_mappings.IsEmpty()) { + DefaultValueFormat label(strm); + strm.PutCString("empty"); + } + if (!m_path_mappings.IsEmpty()) + strm.PutCString("\n"); + } m_path_mappings.Dump(&strm); } } diff --git a/lldb/source/Interpreter/OptionValueRegex.cpp b/lldb/source/Interpreter/OptionValueRegex.cpp index 91ec41d..30e307a 100644 --- a/lldb/source/Interpreter/OptionValueRegex.cpp +++ b/lldb/source/Interpreter/OptionValueRegex.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueRegex.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -24,6 +25,12 @@ void OptionValueRegex::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, llvm::StringRef regex_text = m_regex.GetText(); strm.Printf("%s", regex_text.str().c_str()); } + if (dump_mask & eDumpOptionDefaultValue && + m_regex.GetText() != m_default_regex_str && + !m_default_regex_str.empty()) { + DefaultValueFormat label(strm); + strm.PutCString(m_default_regex_str); + } } } diff --git a/lldb/source/Interpreter/OptionValueSInt64.cpp b/lldb/source/Interpreter/OptionValueSInt64.cpp index df7aee9..00f1cc3 100644 --- a/lldb/source/Interpreter/OptionValueSInt64.cpp +++ b/lldb/source/Interpreter/OptionValueSInt64.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -26,6 +27,11 @@ void OptionValueSInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.Printf("%" PRIi64, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.Printf("%" PRIi64, m_default_value); + } } } diff --git a/lldb/source/Interpreter/OptionValueString.cpp b/lldb/source/Interpreter/OptionValueString.cpp index ae30661a..022a27d 100644 --- a/lldb/source/Interpreter/OptionValueString.cpp +++ b/lldb/source/Interpreter/OptionValueString.cpp @@ -9,12 +9,28 @@ #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Host/OptionParser.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Args.h" #include "lldb/Utility/Stream.h" using namespace lldb; using namespace lldb_private; +static void DumpString(Stream &strm, const std::string &str, bool escape, + bool raw) { + if (escape) { + std::string escaped_str; + Args::ExpandEscapedCharacters(str.c_str(), escaped_str); + DumpString(strm, escaped_str, false, raw); + return; + } + + if (raw) + strm.PutCString(str); + else + strm.QuotedCString(str.c_str()); +} + void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) { if (dump_mask & eDumpOptionType) @@ -22,21 +38,15 @@ void OptionValueString::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionValue) { if (dump_mask & eDumpOptionType) strm.PutCString(" = "); - if (!m_current_value.empty() || m_value_was_set) { - if (m_options.Test(eOptionEncodeCharacterEscapeSequences)) { - std::string expanded_escape_value; - Args::ExpandEscapedCharacters(m_current_value.c_str(), - expanded_escape_value); - if (dump_mask & eDumpOptionRaw) - strm.Printf("%s", expanded_escape_value.c_str()); - else - strm.Printf("\"%s\"", expanded_escape_value.c_str()); - } else { - if (dump_mask & eDumpOptionRaw) - strm.Printf("%s", m_current_value.c_str()); - else - strm.Printf("\"%s\"", m_current_value.c_str()); - } + const bool escape = m_options.Test(eOptionEncodeCharacterEscapeSequences); + const bool raw = dump_mask & eDumpOptionRaw; + if (!m_current_value.empty() || m_value_was_set) + DumpString(strm, m_current_value, escape, raw); + + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value && !m_default_value.empty()) { + DefaultValueFormat label(strm); + DumpString(strm, m_default_value, escape, raw); } } } diff --git a/lldb/source/Interpreter/OptionValueUInt64.cpp b/lldb/source/Interpreter/OptionValueUInt64.cpp index aa5e9a2..63f83cb 100644 --- a/lldb/source/Interpreter/OptionValueUInt64.cpp +++ b/lldb/source/Interpreter/OptionValueUInt64.cpp @@ -8,6 +8,7 @@ #include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Interpreter/OptionValue.h" #include "lldb/Utility/Stream.h" using namespace lldb; @@ -30,6 +31,11 @@ void OptionValueUInt64::DumpValue(const ExecutionContext *exe_ctx, Stream &strm, if (dump_mask & eDumpOptionType) strm.PutCString(" = "); strm.Printf("%" PRIu64, m_current_value); + if (dump_mask & eDumpOptionDefaultValue && + m_current_value != m_default_value) { + DefaultValueFormat label(strm); + strm.Printf("%" PRIu64, m_default_value); + } } } diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp index 4cf68db..ec72542 100644 --- a/lldb/source/Interpreter/Options.cpp +++ b/lldb/source/Interpreter/Options.cpp @@ -19,6 +19,7 @@ #include "lldb/Interpreter/CommandObject.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/DiagnosticsRendering.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/STLExtras.h" @@ -261,7 +262,8 @@ Option *Options::GetLongOptions() { void Options::OutputFormattedUsageText(Stream &strm, const OptionDefinition &option_def, - uint32_t output_max_columns) { + uint32_t output_max_columns, + bool use_color) { std::string actual_text; if (option_def.validator) { const char *condition = option_def.validator->ShortConditionString(); @@ -278,7 +280,7 @@ void Options::OutputFormattedUsageText(Stream &strm, if (static_cast<uint32_t>(actual_text.length() + strm.GetIndentLevel()) < output_max_columns) { // Output it as a single line. - strm.Indent(actual_text); + strm.Indent(ansi::FormatAnsiTerminalCodes(actual_text, use_color)); strm.EOL(); } else { // We need to break it up into multiple lines. @@ -312,7 +314,8 @@ void Options::OutputFormattedUsageText(Stream &strm, strm.Indent(); assert(start < final_end); assert(start + sub_len <= final_end); - strm.Write(actual_text.c_str() + start, sub_len); + strm.PutCString(ansi::FormatAnsiTerminalCodes( + llvm::StringRef(actual_text.c_str() + start, sub_len), use_color)); start = end + 1; } strm.EOL(); @@ -385,7 +388,7 @@ static bool PrintOption(const OptionDefinition &opt_def, } void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd, - uint32_t screen_width) { + uint32_t screen_width, bool use_color) { auto opt_defs = GetDefinitions(); const uint32_t save_indent_level = strm.GetIndentLevel(); llvm::StringRef name = cmd.GetCommandName(); @@ -527,7 +530,7 @@ void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd, strm.IndentMore(5); if (opt_def.usage_text) - OutputFormattedUsageText(strm, opt_def, screen_width); + OutputFormattedUsageText(strm, opt_def, screen_width, use_color); if (!opt_def.enum_values.empty()) { strm.Indent(); strm.Printf("Values: "); diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp index fe9f5d0..1d210ea 100644 --- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -1561,7 +1561,8 @@ void DynamicLoaderDarwinKernel::SetNotificationBreakpointIfNeeded() { .CreateBreakpoint(&module_spec_list, nullptr, "OSKextLoadedKextSummariesUpdated", eFunctionNameTypeFull, eLanguageTypeUnknown, 0, - skip_prologue, internal_bp, hardware) + /*offset_is_insn_count = */ false, skip_prologue, + internal_bp, hardware) .get(); bp->SetCallback(DynamicLoaderDarwinKernel::BreakpointHitCallback, this, diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp index 08bef49..efb9ccc 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOS.cpp @@ -530,7 +530,7 @@ bool DynamicLoaderMacOS::SetNotificationBreakpoint() { m_process->GetTarget() .CreateBreakpoint(&dyld_filelist, source_files, "lldb_image_notifier", eFunctionNameTypeFull, - eLanguageTypeUnknown, 0, skip_prologue, + eLanguageTypeUnknown, 0, false, skip_prologue, internal, hardware) .get(); breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this, @@ -546,8 +546,9 @@ bool DynamicLoaderMacOS::SetNotificationBreakpoint() { m_process->GetTarget() .CreateBreakpoint(&dyld_filelist, source_files, "gdb_image_notifier", eFunctionNameTypeFull, - eLanguageTypeUnknown, 0, skip_prologue, - internal, hardware) + eLanguageTypeUnknown, 0, + /*offset_is_insn_count = */ false, + skip_prologue, internal, hardware) .get(); breakpoint->SetCallback(DynamicLoaderMacOS::NotifyBreakpointHit, this, true); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp index 2529e78..92094c0 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.cpp @@ -123,6 +123,12 @@ private: decl->setDeclContext(decl->getASTContext().getTranslationUnitDecl()); decl->setLexicalDeclContext(decl->getASTContext().getTranslationUnitDecl()); + // Changing the DeclContext might change the linkage. For example, if the + // entity was previously declared inside a function, it will not be + // external, but changing the declaration context to the TU will make it + // external. Make sure this will recompute the linkage if it was computed + // before. + decl->invalidateCachedLinkage(); } bool ChainPassesThrough( @@ -229,6 +235,35 @@ class CompleteTagDeclsScope : public ClangASTImporter::NewDeclListener { clang::ASTContext *m_src_ctx; ClangASTImporter &importer; + void CompleteDecl( + Decl *decl, + lldb_private::ClangASTImporter::ASTContextMetadata const &to_context_md) { + // The decl that should be completed has to be imported into the target + // context from some other context. + assert(to_context_md.hasOrigin(decl)); + // We should only complete decls coming from the source context. + assert(to_context_md.getOrigin(decl).ctx == m_src_ctx); + + Decl *original_decl = to_context_md.getOrigin(decl).decl; + + // Complete the decl now. + TypeSystemClang::GetCompleteDecl(m_src_ctx, original_decl); + if (auto *tag_decl = dyn_cast<TagDecl>(decl)) { + if (auto *original_tag_decl = dyn_cast<TagDecl>(original_decl)) { + if (original_tag_decl->isCompleteDefinition()) { + m_delegate->ImportDefinitionTo(tag_decl, original_tag_decl); + tag_decl->setCompleteDefinition(true); + } + } + + tag_decl->setHasExternalLexicalStorage(false); + tag_decl->setHasExternalVisibleStorage(false); + } else if (auto *container_decl = dyn_cast<ObjCContainerDecl>(decl)) { + container_decl->setHasExternalLexicalStorage(false); + container_decl->setHasExternalVisibleStorage(false); + } + } + public: /// Constructs a CompleteTagDeclsScope. /// \param importer The ClangASTImporter that we should observe. @@ -251,30 +286,7 @@ public: NamedDecl *decl = m_decls_to_complete.pop_back_val(); m_decls_already_completed.insert(decl); - // The decl that should be completed has to be imported into the target - // context from some other context. - assert(to_context_md->hasOrigin(decl)); - // We should only complete decls coming from the source context. - assert(to_context_md->getOrigin(decl).ctx == m_src_ctx); - - Decl *original_decl = to_context_md->getOrigin(decl).decl; - - // Complete the decl now. - TypeSystemClang::GetCompleteDecl(m_src_ctx, original_decl); - if (auto *tag_decl = dyn_cast<TagDecl>(decl)) { - if (auto *original_tag_decl = dyn_cast<TagDecl>(original_decl)) { - if (original_tag_decl->isCompleteDefinition()) { - m_delegate->ImportDefinitionTo(tag_decl, original_tag_decl); - tag_decl->setCompleteDefinition(true); - } - } - - tag_decl->setHasExternalLexicalStorage(false); - tag_decl->setHasExternalVisibleStorage(false); - } else if (auto *container_decl = dyn_cast<ObjCContainerDecl>(decl)) { - container_decl->setHasExternalLexicalStorage(false); - container_decl->setHasExternalVisibleStorage(false); - } + CompleteDecl(decl, *to_context_md); to_context_md->removeOrigin(decl); } @@ -320,7 +332,8 @@ CompilerType ClangASTImporter::DeportType(TypeSystemClang &dst, DeclContextOverride decl_context_override; if (auto *t = ClangUtil::GetQualType(src_type)->getAs<TagType>()) - decl_context_override.OverrideAllDeclsFromContainingFunction(t->getDecl()); + decl_context_override.OverrideAllDeclsFromContainingFunction( + t->getOriginalDecl()); CompleteTagDeclsScope complete_scope(*this, &dst.getASTContext(), &src_ctxt->getASTContext()); @@ -358,6 +371,16 @@ clang::Decl *ClangASTImporter::DeportDecl(clang::ASTContext *dst_ctx, return result; } +bool ClangASTImporter::CanImport(const Decl *d) { + if (!d) + return false; + if (isa<TagDecl>(d)) + return GetDeclOrigin(d).Valid(); + if (isa<ObjCInterfaceDecl>(d)) + return GetDeclOrigin(d).Valid(); + return false; +} + bool ClangASTImporter::CanImport(const CompilerType &type) { if (!ClangUtil::IsClangType(type)) return false; @@ -367,24 +390,10 @@ bool ClangASTImporter::CanImport(const CompilerType &type) { const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { - case clang::Type::Record: { - const clang::CXXRecordDecl *cxx_record_decl = - qual_type->getAsCXXRecordDecl(); - if (cxx_record_decl) { - if (GetDeclOrigin(cxx_record_decl).Valid()) - return true; - } - } break; - - case clang::Type::Enum: { - clang::EnumDecl *enum_decl = - llvm::cast<clang::EnumType>(qual_type)->getDecl(); - if (enum_decl) { - if (GetDeclOrigin(enum_decl).Valid()) - return true; - } - } break; - + case clang::Type::Record: + return CanImport(qual_type->getAsCXXRecordDecl()); + case clang::Type::Enum: + return CanImport(llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl()); case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = @@ -394,10 +403,7 @@ bool ClangASTImporter::CanImport(const CompilerType &type) { 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 (GetDeclOrigin(class_interface_decl).Valid()) - return true; - } + return CanImport(class_interface_decl); } } break; @@ -414,12 +420,6 @@ bool ClangASTImporter::CanImport(const CompilerType &type) { ->getDeducedType() .getAsOpaquePtr())); - case clang::Type::Elaborated: - return CanImport(CompilerType(type.GetTypeSystem(), - llvm::cast<clang::ElaboratedType>(qual_type) - ->getNamedType() - .getAsOpaquePtr())); - case clang::Type::Paren: return CanImport(CompilerType( type.GetTypeSystem(), @@ -452,7 +452,7 @@ bool ClangASTImporter::Import(const CompilerType &type) { case clang::Type::Enum: { clang::EnumDecl *enum_decl = - llvm::cast<clang::EnumType>(qual_type)->getDecl(); + llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl(); if (enum_decl) { if (GetDeclOrigin(enum_decl).Valid()) return CompleteAndFetchChildren(qual_type); @@ -488,12 +488,6 @@ bool ClangASTImporter::Import(const CompilerType &type) { ->getDeducedType() .getAsOpaquePtr())); - case clang::Type::Elaborated: - return Import(CompilerType(type.GetTypeSystem(), - llvm::cast<clang::ElaboratedType>(qual_type) - ->getNamedType() - .getAsOpaquePtr())); - case clang::Type::Paren: return Import(CompilerType( type.GetTypeSystem(), @@ -597,7 +591,7 @@ bool ExtractBaseOffsets(const ASTRecordLayout &record_layout, return false; DeclFromUser<RecordDecl> origin_base_record( - origin_base_record_type->getDecl()); + origin_base_record_type->getOriginalDecl()); if (origin_base_record.IsInvalid()) return false; @@ -728,7 +722,8 @@ bool ClangASTImporter::importRecordLayoutFromOrigin( QualType base_type = bi->getType(); const RecordType *base_record_type = base_type->getAs<RecordType>(); - DeclFromParser<RecordDecl> base_record(base_record_type->getDecl()); + DeclFromParser<RecordDecl> base_record( + base_record_type->getOriginalDecl()); DeclFromParser<CXXRecordDecl> base_cxx_record = DynCast<CXXRecordDecl>(base_record); @@ -860,7 +855,7 @@ bool ClangASTImporter::CompleteAndFetchChildren(clang::QualType type) { Log *log = GetLog(LLDBLog::Expressions); if (const TagType *tag_type = type->getAs<TagType>()) { - TagDecl *tag_decl = tag_type->getDecl(); + TagDecl *tag_decl = tag_type->getOriginalDecl(); DeclOrigin decl_origin = GetDeclOrigin(tag_decl); @@ -928,9 +923,9 @@ bool ClangASTImporter::RequireCompleteType(clang::QualType type) { return false; if (const TagType *tag_type = type->getAs<TagType>()) { - TagDecl *tag_decl = tag_type->getDecl(); + TagDecl *tag_decl = tag_type->getOriginalDecl(); - if (tag_decl->getDefinition() || tag_decl->isBeingDefined()) + if (tag_decl->getDefinition()) return true; return CompleteTagDecl(tag_decl); @@ -1053,6 +1048,16 @@ ClangASTImporter::MapCompleter::~MapCompleter() = default; llvm::Expected<Decl *> ClangASTImporter::ASTImporterDelegate::ImportImpl(Decl *From) { + // FIXME: The Minimal import mode of clang::ASTImporter does not correctly + // import Lambda definitions. Work around this for now by not importing + // lambdas at all. This is most likely encountered when importing decls from + // the `std` module (not from debug-info), where lambdas can be defined in + // inline function bodies. Those will be imported by LLDB. + if (const auto *CXX = llvm::dyn_cast<clang::CXXRecordDecl>(From)) + if (CXX->isLambda()) + return llvm::make_error<ASTImportError>( + ASTImportError::UnsupportedConstruct); + if (m_std_handler) { std::optional<Decl *> D = m_std_handler->Import(From); if (D) { @@ -1375,6 +1380,18 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from, from, m_source_ctx, &to->getASTContext()); } + if (auto *to_namespace_decl = dyn_cast<NamespaceDecl>(to)) { + m_main.BuildNamespaceMap(to_namespace_decl); + to_namespace_decl->setHasExternalVisibleStorage(); + } + + MarkDeclImported(from, to); +} + +void ClangASTImporter::ASTImporterDelegate::MarkDeclImported(Decl *from, + Decl *to) { + Log *log = GetLog(LLDBLog::Expressions); + if (auto *to_tag_decl = dyn_cast<TagDecl>(to)) { to_tag_decl->setHasExternalLexicalStorage(); to_tag_decl->getPrimaryContext()->setMustBuildLookupTable(); @@ -1389,11 +1406,6 @@ void ClangASTImporter::ASTImporterDelegate::Imported(clang::Decl *from, (to_tag_decl->isCompleteDefinition() ? "complete" : "incomplete")); } - if (auto *to_namespace_decl = dyn_cast<NamespaceDecl>(to)) { - m_main.BuildNamespaceMap(to_namespace_decl); - to_namespace_decl->setHasExternalVisibleStorage(); - } - if (auto *to_container_decl = dyn_cast<ObjCContainerDecl>(to)) { to_container_decl->setHasExternalLexicalStorage(); to_container_decl->setHasExternalVisibleStorage(); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h index 47b137a..03d2556 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTImporter.h @@ -157,6 +157,8 @@ public: /// \see ClangASTImporter::Import bool CanImport(const CompilerType &type); + bool CanImport(const clang::Decl *d); + /// If the given type was copied from another TypeSystemClang then copy over /// all missing information (e.g., the definition of a 'class' type). /// @@ -346,6 +348,8 @@ public: llvm::Expected<clang::Decl *> ImportImpl(clang::Decl *From) override; private: + void MarkDeclImported(clang::Decl *from, clang::Decl *to); + /// Decls we should ignore when mapping decls back to their original /// ASTContext. Used by the CxxModuleHandler to mark declarations that /// were created from the 'std' C++ module to prevent that the Importer diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp index 4b52f6a..21a9307 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangASTSource.cpp @@ -223,7 +223,7 @@ TagDecl *ClangASTSource::FindCompleteType(const TagDecl *decl) { continue; TagDecl *candidate_tag_decl = - const_cast<TagDecl *>(tag_type->getDecl()); + tag_type->getOriginalDecl()->getDefinitionOrSelf(); if (TypeSystemClang::GetCompleteDecl( &candidate_tag_decl->getASTContext(), candidate_tag_decl)) @@ -250,7 +250,8 @@ TagDecl *ClangASTSource::FindCompleteType(const TagDecl *decl) { if (!tag_type) continue; - TagDecl *candidate_tag_decl = const_cast<TagDecl *>(tag_type->getDecl()); + TagDecl *candidate_tag_decl = + tag_type->getOriginalDecl()->getDefinitionOrSelf(); if (TypeSystemClang::GetCompleteDecl(&candidate_tag_decl->getASTContext(), candidate_tag_decl)) diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index 214e260..8a68282 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -839,7 +839,7 @@ void ClangExpressionDeclMap::LookUpLldbClass(NameSearchContext &context) { clang::CXXRecordDecl *class_decl = method_decl->getParent(); - QualType class_qual_type(class_decl->getTypeForDecl(), 0); + QualType class_qual_type = m_ast_context->getCanonicalTagType(class_decl); TypeFromUser class_user_type( class_qual_type.getAsOpaquePtr(), @@ -1561,7 +1561,7 @@ ClangExpressionDeclMap::AddExpressionVariable(NameSearchContext &context, if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) { if (const TagType *tag_type = dyn_cast<TagType>(parser_type)) - CompleteType(tag_type->getDecl()); + CompleteType(tag_type->getOriginalDecl()->getDefinitionOrSelf()); if (const ObjCObjectPointerType *objc_object_ptr_type = dyn_cast<ObjCObjectPointerType>(parser_type)) CompleteType(objc_object_ptr_type->getInterfaceDecl()); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp index aa0e6e3..319ff3fe8 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangPersistentVariables.cpp @@ -79,9 +79,11 @@ ClangPersistentVariables::GetCompilerTypeFromPersistentDecl( if (p.m_decl == nullptr) return std::nullopt; + auto ctx = std::static_pointer_cast<TypeSystemClang>(p.m_context.lock()); if (clang::TypeDecl *tdecl = llvm::dyn_cast<clang::TypeDecl>(p.m_decl)) { - opaque_compiler_type_t t = static_cast<opaque_compiler_type_t>( - const_cast<clang::Type *>(tdecl->getTypeForDecl())); + opaque_compiler_type_t t = + static_cast<opaque_compiler_type_t>(const_cast<clang::Type *>( + ctx->getASTContext().getTypeDeclType(tdecl).getTypePtr())); return CompilerType(p.m_context, t); } return std::nullopt; diff --git a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp index 45ad4f1..6f57c18 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/NameSearchContext.cpp @@ -153,7 +153,7 @@ NameSearchContext::AddTypeDecl(const CompilerType &clang_type) { return (NamedDecl *)typedef_name_decl; } else if (const TagType *tag_type = qual_type->getAs<TagType>()) { - TagDecl *tag_decl = tag_type->getDecl(); + TagDecl *tag_decl = tag_type->getOriginalDecl()->getDefinitionOrSelf(); m_decls.push_back(tag_decl); diff --git a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp index e67e60b..b89a6aa 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/MainThreadChecker/InstrumentationRuntimeMainThreadChecker.cpp @@ -266,9 +266,9 @@ InstrumentationRuntimeMainThreadChecker::GetBacktracesFromExtendedStopInfo( // We gather symbolication addresses above, so no need for HistoryThread to // try to infer the call addresses. - bool pcs_are_call_addresses = true; - ThreadSP new_thread_sp = std::make_shared<HistoryThread>( - *process_sp, tid, PCs, pcs_are_call_addresses); + auto pc_type = HistoryPCType::Calls; + ThreadSP new_thread_sp = + std::make_shared<HistoryThread>(*process_sp, tid, PCs, pc_type); // Save this in the Process' ExtendedThreadList so a strong pointer retains // the object diff --git a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp index c2db354..565fd35 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/UBSan/InstrumentationRuntimeUBSan.cpp @@ -324,9 +324,9 @@ InstrumentationRuntimeUBSan::GetBacktracesFromExtendedStopInfo( // We gather symbolication addresses above, so no need for HistoryThread to // try to infer the call addresses. - bool pcs_are_call_addresses = true; - ThreadSP new_thread_sp = std::make_shared<HistoryThread>( - *process_sp, tid, PCs, pcs_are_call_addresses); + auto pc_type = HistoryPCType::Calls; + ThreadSP new_thread_sp = + std::make_shared<HistoryThread>(*process_sp, tid, PCs, pc_type); std::string stop_reason_description = GetStopReasonDescription(info); new_thread_sp->SetName(stop_reason_description.c_str()); diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp index 9648924..38c334b 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/ReportRetriever.cpp @@ -83,7 +83,7 @@ ReportRetriever::RetrieveReportData(const ProcessSP process_sp) { options.SetAutoApplyFixIts(false); options.SetLanguage(eLanguageTypeObjC_plus_plus); - if (auto m = GetPreferredAsanModule(process_sp->GetTarget())) { + if (auto [m, _] = GetPreferredAsanModule(process_sp->GetTarget()); m) { SymbolContextList sc_list; sc_list.Append(SymbolContext(std::move(m))); options.SetPreferredSymbolContexts(std::move(sc_list)); diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp index a5cee5d..09c6988 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.cpp @@ -13,10 +13,12 @@ namespace lldb_private { -lldb::ModuleSP GetPreferredAsanModule(const Target &target) { - // Currently only supported on Darwin. +std::tuple<lldb::ModuleSP, HistoryPCType> +GetPreferredAsanModule(const Target &target) { + // Currently only Darwin provides ASan runtime support as part of the OS + // (libsanitizers). if (!target.GetArchitecture().GetTriple().isOSDarwin()) - return nullptr; + return {nullptr, HistoryPCType::Calls}; lldb::ModuleSP module; llvm::Regex pattern(R"(libclang_rt\.asan_.*_dynamic\.dylib)"); @@ -29,7 +31,16 @@ lldb::ModuleSP GetPreferredAsanModule(const Target &target) { return IterationAction::Continue; }); - return module; + // `Calls` - The ASan compiler-rt runtime already massages the return + // addresses into call addresses, so we don't want LLDB's unwinder to try to + // locate the previous instruction again as this might lead to us reporting + // a different line. + // `ReturnsNoZerothFrame` - Darwin, but not ASan compiler-rt implies + // libsanitizers which collects return addresses. It also discards a few + // non-user frames at the top of the stack. + auto pc_type = + (module ? HistoryPCType::Calls : HistoryPCType::ReturnsNoZerothFrame); + return {module, pc_type}; } } // namespace lldb_private diff --git a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h index 425f0a2..e26d2bc 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h +++ b/lldb/source/Plugins/InstrumentationRuntime/Utility/Utility.h @@ -9,18 +9,20 @@ #ifndef LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_UTILITY_UTILITY_H #define LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_UTILITY_UTILITY_H +#include <tuple> + #include "lldb/lldb-forward.h" +#include "lldb/lldb-private-enumerations.h" namespace lldb_private { -class Target; - /// On Darwin, if LLDB loaded libclang_rt, it's coming from a locally built /// compiler-rt, and we should prefer it in favour of the system sanitizers /// when running InstrumentationRuntime utility expressions that use symbols /// from the sanitizer libraries. This helper searches the target for such a /// dylib. Returns nullptr if no such dylib was found. -lldb::ModuleSP GetPreferredAsanModule(const Target &target); +std::tuple<lldb::ModuleSP, HistoryPCType> +GetPreferredAsanModule(const Target &target); } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 3118ff1..c39b529 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -105,7 +105,9 @@ CPlusPlusLanguage::GetFunctionNameInfo(ConstString name) const { bool CPlusPlusLanguage::SymbolNameFitsToLanguage(Mangled mangled) const { const char *mangled_name = mangled.GetMangledName().GetCString(); - return mangled_name && Mangled::IsMangledName(mangled_name); + auto mangling_scheme = Mangled::GetManglingScheme(mangled_name); + return mangled_name && (mangling_scheme == Mangled::eManglingSchemeItanium || + mangling_scheme == Mangled::eManglingSchemeMSVC); } ConstString CPlusPlusLanguage::GetDemangledFunctionNameWithoutArguments( @@ -602,126 +604,6 @@ bool CPlusPlusLanguage::ExtractContextAndIdentifier( return false; } -namespace { -class NodeAllocator { - llvm::BumpPtrAllocator Alloc; - -public: - void reset() { Alloc.Reset(); } - - template <typename T, typename... Args> T *makeNode(Args &&...args) { - return new (Alloc.Allocate(sizeof(T), alignof(T))) - T(std::forward<Args>(args)...); - } - - void *allocateNodeArray(size_t sz) { - return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, - alignof(llvm::itanium_demangle::Node *)); - } -}; - -template <typename Derived> -class ManglingSubstitutor - : public llvm::itanium_demangle::AbstractManglingParser<Derived, - NodeAllocator> { - using Base = - llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; - -public: - ManglingSubstitutor() : Base(nullptr, nullptr) {} - - template <typename... Ts> - ConstString substitute(llvm::StringRef Mangled, Ts &&...Vals) { - this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); - return substituteImpl(Mangled); - } - -protected: - void reset(llvm::StringRef Mangled) { - Base::reset(Mangled.begin(), Mangled.end()); - Written = Mangled.begin(); - Result.clear(); - Substituted = false; - } - - ConstString substituteImpl(llvm::StringRef Mangled) { - Log *log = GetLog(LLDBLog::Language); - if (this->parse() == nullptr) { - LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled); - return ConstString(); - } - if (!Substituted) - return ConstString(); - - // Append any trailing unmodified input. - appendUnchangedInput(); - LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); - return ConstString(Result); - } - - void trySubstitute(llvm::StringRef From, llvm::StringRef To) { - if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) - return; - - // We found a match. Append unmodified input up to this point. - appendUnchangedInput(); - - // And then perform the replacement. - Result += To; - Written += From.size(); - Substituted = true; - } - -private: - /// Input character until which we have constructed the respective output - /// already. - const char *Written = ""; - - llvm::SmallString<128> Result; - - /// Whether we have performed any substitutions. - bool Substituted = false; - - const char *currentParserPos() const { return this->First; } - - void appendUnchangedInput() { - Result += - llvm::StringRef(Written, std::distance(Written, currentParserPos())); - Written = currentParserPos(); - } -}; - -/// Given a mangled function `Mangled`, replace all the primitive function type -/// arguments of `Search` with type `Replace`. -class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { - llvm::StringRef Search; - llvm::StringRef Replace; - -public: - void reset(llvm::StringRef Mangled, llvm::StringRef Search, - llvm::StringRef Replace) { - ManglingSubstitutor::reset(Mangled); - this->Search = Search; - this->Replace = Replace; - } - - llvm::itanium_demangle::Node *parseType() { - trySubstitute(Search, Replace); - return ManglingSubstitutor::parseType(); - } -}; - -class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { -public: - llvm::itanium_demangle::Node * - parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { - trySubstitute("C1", "C2"); - trySubstitute("D1", "D2"); - return ManglingSubstitutor::parseCtorDtorName(SoFar, State); - } -}; -} // namespace - std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( const ConstString mangled_name) const { std::vector<ConstString> alternates; @@ -749,29 +631,49 @@ std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings( alternates.push_back(ConstString(fixed_scratch)); } - TypeSubstitutor TS; + auto *log = GetLog(LLDBLog::Language); + // `char` is implementation defined as either `signed` or `unsigned`. As a // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed // char, 'h'-unsigned char. If we're looking for symbols with a signed char // parameter, try finding matches which have the general case 'c'. - if (ConstString char_fixup = - TS.substitute(mangled_name.GetStringRef(), "a", "c")) - alternates.push_back(char_fixup); + if (auto char_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "a", "c")) { + // LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result); + if (*char_fixup_or_err) + alternates.push_back(*char_fixup_or_err); + } else + LLDB_LOG_ERROR(log, char_fixup_or_err.takeError(), + "Failed to substitute 'char' type mangling: {0}"); // long long parameter mangling 'x', may actually just be a long 'l' argument - if (ConstString long_fixup = - TS.substitute(mangled_name.GetStringRef(), "x", "l")) - alternates.push_back(long_fixup); + if (auto long_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "x", "l")) { + if (*long_fixup_or_err) + alternates.push_back(*long_fixup_or_err); + } else + LLDB_LOG_ERROR(log, long_fixup_or_err.takeError(), + "Failed to substitute 'long long' type mangling: {0}"); // unsigned long long parameter mangling 'y', may actually just be unsigned // long 'm' argument - if (ConstString ulong_fixup = - TS.substitute(mangled_name.GetStringRef(), "y", "m")) - alternates.push_back(ulong_fixup); - - if (ConstString ctor_fixup = - CtorDtorSubstitutor().substitute(mangled_name.GetStringRef())) - alternates.push_back(ctor_fixup); + if (auto ulong_fixup_or_err = + SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "y", "m")) { + if (*ulong_fixup_or_err) + alternates.push_back(*ulong_fixup_or_err); + } else + LLDB_LOG_ERROR( + log, ulong_fixup_or_err.takeError(), + "Failed to substitute 'unsigned long long' type mangling: {0}"); + + if (auto ctor_fixup_or_err = SubstituteStructorAliases_ItaniumMangle( + mangled_name.GetStringRef())) { + if (*ctor_fixup_or_err) { + alternates.push_back(*ctor_fixup_or_err); + } + } else + LLDB_LOG_ERROR(log, ctor_fixup_or_err.takeError(), + "Failed to substitute structor alias manglings: {0}"); return alternates; } @@ -2440,6 +2342,160 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable( } } +namespace { +class NodeAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template <typename T, typename... Args> T *makeNode(Args &&...args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, + alignof(llvm::itanium_demangle::Node *)); + } +}; + +template <typename Derived> +class ManglingSubstitutor + : public llvm::itanium_demangle::AbstractManglingParser<Derived, + NodeAllocator> { + using Base = + llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>; + +public: + ManglingSubstitutor() : Base(nullptr, nullptr) {} + + template <typename... Ts> + llvm::Expected<ConstString> substitute(llvm::StringRef Mangled, + Ts &&...Vals) { + this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...); + return substituteImpl(Mangled); + } + +protected: + void reset(llvm::StringRef Mangled) { + Base::reset(Mangled.begin(), Mangled.end()); + Written = Mangled.begin(); + Result.clear(); + Substituted = false; + } + + llvm::Expected<ConstString> substituteImpl(llvm::StringRef Mangled) { + if (this->parse() == nullptr) + return llvm::createStringError( + llvm::formatv("Failed to substitute mangling in '{0}'", Mangled)); + + if (!Substituted) + return ConstString(); + + // Append any trailing unmodified input. + appendUnchangedInput(); + return ConstString(Result); + } + + void trySubstitute(llvm::StringRef From, llvm::StringRef To) { + if (!llvm::StringRef(currentParserPos(), this->numLeft()).starts_with(From)) + return; + + // We found a match. Append unmodified input up to this point. + appendUnchangedInput(); + + // And then perform the replacement. + Result += To; + Written += From.size(); + Substituted = true; + } + +private: + /// Input character until which we have constructed the respective output + /// already. + const char *Written = ""; + + llvm::SmallString<128> Result; + + /// Whether we have performed any substitutions. + bool Substituted = false; + + const char *currentParserPos() const { return this->First; } + + void appendUnchangedInput() { + Result += + llvm::StringRef(Written, std::distance(Written, currentParserPos())); + Written = currentParserPos(); + } +}; + +/// Given a mangled function `Mangled`, replace all the primitive function type +/// arguments of `Search` with type `Replace`. +class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + llvm::itanium_demangle::Node *parseType() { + trySubstitute(Search, Replace); + return ManglingSubstitutor::parseType(); + } +}; + +class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + + void reset(llvm::StringRef Mangled) { ManglingSubstitutor::reset(Mangled); } + + llvm::itanium_demangle::Node * + parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { + if (!Search.empty() && !Replace.empty()) { + trySubstitute(Search, Replace); + } else { + trySubstitute("D1", "D2"); + trySubstitute("C1", "C2"); + } + return ManglingSubstitutor::parseCtorDtorName(SoFar, State); + } +}; +} // namespace + +llvm::Expected<ConstString> +CPlusPlusLanguage::SubstituteType_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to) { + return TypeSubstitutor().substitute(mangled_name, subst_from, subst_to); +} + +llvm::Expected<ConstString> CPlusPlusLanguage::SubstituteStructor_ItaniumMangle( + llvm::StringRef mangled_name, llvm::StringRef subst_from, + llvm::StringRef subst_to) { + return CtorDtorSubstitutor().substitute(mangled_name, subst_from, subst_to); +} + +llvm::Expected<ConstString> +CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle( + llvm::StringRef mangled_name) { + return CtorDtorSubstitutor().substitute(mangled_name); +} + #define LLDB_PROPERTIES_language_cplusplus #include "LanguageCPlusPlusProperties.inc" diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 4a30299..9a528ca 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -164,6 +164,76 @@ public: ConstString FindBestAlternateFunctionMangledName( const Mangled mangled, const SymbolContext &sym_ctx) const override; + /// Substitutes Itanium type encoding substrings given by \c subst_from + /// in \c mangled_name with \c subst_to. + /// + /// This function will only replace Itanium type encodings (i.e., <type> + /// productions in the Itanium ABI mangling grammar). However, no verifiction + /// is done on whether \c subst_from or \c subst_to is a valid type encoding. + /// + /// \param[in] mangled_name Mangled name to perform the substitutions in. + /// This function only supports Itanium ABI mangling. + /// + /// \param[in] subst_from The substring to substitute. + /// + /// \param[in] subst_to The substring to insert. + /// + /// \returns The mangled string with substitutions. If no substitutions + /// have been made, returns an empty \c ConstString (even if the string + /// already contained the substitutions). If an error occurred, this function + /// returns the error. + /// + static llvm::Expected<ConstString> + SubstituteType_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to); + + /// Substitutes Itanium structor encoding substrings given by \c subst_from + /// in \c mangled_name with \c subst_to. + /// + /// This function will only replace Itanium structor encodings (i.e., + /// <ctor-dtor-name> productions in the Itanium ABI mangling grammar). + /// However, no verifiction is done on whether \c subst_from or \c subst_to is + /// a valid structor encoding. + /// + /// \param[in] mangled_name Mangled name to perform the substitutions in. + /// This function only supports Itanium ABI mangling. + /// + /// \param[in] subst_from The substring to substitute. + /// + /// \param[in] subst_to The substring to insert. + /// + /// \returns The mangled string with substitutions. If no substitutions + /// have been made, returns an empty \c ConstString (even if the string + /// already contained the substitutions). If an error occurred, this function + /// returns the error. + /// + static llvm::Expected<ConstString> + SubstituteStructor_ItaniumMangle(llvm::StringRef mangled_name, + llvm::StringRef subst_from, + llvm::StringRef subst_to); + + /// Tries replacing Itanium structor encoding substrings in \c mangled_name + /// with potential aliases.j + /// + /// This function will only replace Itanium structor encodings (i.e., + /// <ctor-dtor-name> productions in the Itanium ABI mangling grammar). + /// + /// E.g., on some platforms, the C1/D1 variants are aliased to the C2/D2 + /// variants. This function will try to replace occurrences of C1/D1 with + /// C2/D2. + /// + /// \param[in] mangled_name Mangled name to perform the substitutions in. + /// This function only supports Itanium ABI mangling. + /// + /// \returns The mangled string with substitutions. If no substitutions + /// have been made, returns an empty \c ConstString (even if the string + /// already contained the substitutions). If an error occurred, this function + /// returns the error. + /// + static llvm::Expected<ConstString> + SubstituteStructorAliases_ItaniumMangle(llvm::StringRef mangled_name); + llvm::StringRef GetInstanceVariableName() override { return "this"; } FormatEntity::Entry GetFunctionNameFormat() const override; diff --git a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp index ea1edbf..5289027 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/GenericList.cpp @@ -339,11 +339,18 @@ lldb::ChildCacheState LibCxxForwardListFrontEnd::Update() { if (err.Fail() || !backend_addr) return lldb::ChildCacheState::eRefetch; - ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_")); + auto list_base_sp = m_backend.GetChildAtIndex(0); + if (!list_base_sp) + return lldb::ChildCacheState::eRefetch; + + // Anonymous strucutre index is in base class at index 0. + auto [impl_sp, is_compressed_pair] = + GetValueOrOldCompressedPair(*list_base_sp, /*anon_struct_idx=*/0, + "__before_begin_", "__before_begin_"); if (!impl_sp) return ChildCacheState::eRefetch; - if (isOldCompressedPairLayout(*impl_sp)) + if (is_compressed_pair) impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp); if (!impl_sp) @@ -366,17 +373,10 @@ llvm::Expected<uint32_t> LibCxxListFrontEnd::CalculateNumChildren() { if (!m_head || !m_tail || m_node_address == 0) return 0; - ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_")); - if (!size_node_sp) { - size_node_sp = m_backend.GetChildMemberWithName( - "__size_alloc_"); // pre-compressed_pair rework - - if (!isOldCompressedPairLayout(*size_node_sp)) - return llvm::createStringError("Unexpected std::list layout: expected " - "old __compressed_pair layout."); - + auto [size_node_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + m_backend, /*anon_struct_idx=*/1, "__size_", "__size_alloc_"); + if (is_compressed_pair) size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp); - } if (size_node_sp) m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX); diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index a787404..6053d04 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -49,11 +49,6 @@ static void consumeInlineNamespace(llvm::StringRef &name) { } } -bool lldb_private::formatters::isOldCompressedPairLayout( - ValueObject &pair_obj) { - return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair"); -} - bool lldb_private::formatters::isStdTemplate(ConstString type_name, llvm::StringRef type) { llvm::StringRef name = type_name.GetStringRef(); @@ -105,6 +100,44 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair( return value; } +std::pair<lldb::ValueObjectSP, bool> +lldb_private::formatters::GetValueOrOldCompressedPair( + ValueObject &obj, size_t anon_struct_idx, llvm::StringRef child_name, + llvm::StringRef compressed_pair_name) { + auto is_old_compressed_pair = [](ValueObject &pair_obj) -> bool { + return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair"); + }; + + // Try searching the child member in an anonymous structure first. + if (auto unwrapped = obj.GetChildAtIndex(anon_struct_idx)) { + ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name)); + if (node_sp) + return {node_sp, is_old_compressed_pair(*node_sp)}; + } + + // Older versions of libc++ don't wrap the children in anonymous structures. + // Try that instead. + ValueObjectSP node_sp(obj.GetChildMemberWithName(child_name)); + if (node_sp) + return {node_sp, is_old_compressed_pair(*node_sp)}; + + // Try the even older __compressed_pair layout. + + assert(!compressed_pair_name.empty()); + + node_sp = obj.GetChildMemberWithName(compressed_pair_name); + + // Unrecognized layout (possibly older than LLDB supports). + if (!node_sp) + return {nullptr, false}; + + // Expected old compressed_pair layout, but got something else. + if (!is_old_compressed_pair(*node_sp)) + return {nullptr, false}; + + return {node_sp, true}; +} + bool lldb_private::formatters::LibcxxFunctionSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { @@ -205,11 +238,12 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider( if (!valobj_sp) return false; - ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_"); if (!ptr_sp) return false; - if (isOldCompressedPairLayout(*ptr_sp)) + if (is_compressed_pair) ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp); if (!ptr_sp) @@ -379,13 +413,14 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() { if (!valobj_sp) return lldb::ChildCacheState::eRefetch; - ValueObjectSP ptr_sp(valobj_sp->GetChildMemberWithName("__ptr_")); + auto [ptr_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *valobj_sp, /*anon_struct_idx=*/0, "__ptr_", "__ptr_"); if (!ptr_sp) return lldb::ChildCacheState::eRefetch; // Retrieve the actual pointer and the deleter, and clone them to give them // user-friendly names. - if (isOldCompressedPairLayout(*ptr_sp)) { + if (is_compressed_pair) { if (ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp)) m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer")); @@ -424,17 +459,15 @@ enum class StringLayout { CSD, DSC }; } static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) { - if (auto rep_sp = valobj.GetChildMemberWithName("__rep_")) - return rep_sp; - - ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_"); - if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) + auto [valobj_r_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + valobj, /*anon_struct_idx=*/0, "__rep_", "__r_"); + if (!valobj_r_sp) return nullptr; - if (!isOldCompressedPairLayout(*valobj_r_sp)) - return nullptr; + if (is_compressed_pair) + return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp); - return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp); + return valobj_r_sp; } /// Determine the size in bytes of \p valobj (a libc++ std::string object) and diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index d88a6ec..819f8a9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -25,7 +25,22 @@ GetChildMemberWithName(ValueObject &obj, lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair); lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair); -bool isOldCompressedPairLayout(ValueObject &pair_obj); + +/// Returns the ValueObjectSP of the child of \c obj. If \c obj has no +/// child named \c child_name, returns the __compressed_pair child instead +/// with \c compressed_pair_name, if one exists. +/// +/// Latest libc++ wrap the compressed children in an anonymous structure. +/// The \c anon_struct_idx indicates the location of this struct. +/// +/// The returned boolean is \c true if the returned child was has an old-style +/// libc++ __compressed_pair layout. +/// +/// If no child was found returns a nullptr. +std::pair<lldb::ValueObjectSP, bool> +GetValueOrOldCompressedPair(ValueObject &obj, size_t anon_struct_idx, + llvm::StringRef child_name, + llvm::StringRef compressed_pair_name); bool isStdTemplate(ConstString type_name, llvm::StringRef type); bool LibcxxStringSummaryProviderASCII( diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp index 41441df..8576696 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp @@ -200,7 +200,8 @@ public: llvm::Expected<size_t> GetIndexOfChildWithName(ConstString name) override; private: - llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout(); + llvm::Expected<uint32_t> + CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair); /// Returns the ValueObject for the __tree_node type that /// holds the key/value pair of the node at index \ref idx. @@ -254,16 +255,8 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: llvm::Expected<uint32_t> lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd:: - CalculateNumChildrenForOldCompressedPairLayout() { - ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_")); - if (!node_sp) - return 0; - - if (!isOldCompressedPairLayout(*node_sp)) - return llvm::createStringError("Unexpected std::map layout: expected " - "old __compressed_pair layout."); - - node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp); + CalculateNumChildrenForOldCompressedPairLayout(ValueObject &pair) { + auto node_sp = GetFirstValueOfLibCXXCompressedPair(pair); if (!node_sp) return 0; @@ -281,12 +274,16 @@ llvm::Expected<uint32_t> lldb_private::formatters:: if (m_tree == nullptr) return 0; - if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) { - m_count = node_sp->GetValueAsUnsigned(0); - return m_count; - } + auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *m_tree, /*anon_struct_idx=*/2, "__size_", "__pair3_"); + if (!size_sp) + return llvm::createStringError("Unexpected std::map layout"); - return CalculateNumChildrenForOldCompressedPairLayout(); + if (is_compressed_pair) + return CalculateNumChildrenForOldCompressedPairLayout(*size_sp); + + m_count = size_sp->GetValueAsUnsigned(0); + return m_count; } ValueObjectSP diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp index 501fd09..f88a531 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp @@ -130,22 +130,17 @@ CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: GetNodeType() { - auto node_sp = m_backend.GetChildAtNamePath({"__table_", "__first_node_"}); - - if (!node_sp) { - auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"}); - if (!p1_sp) - return {}; + auto table_sp = m_backend.GetChildMemberWithName("__table_"); + if (!table_sp) + return {}; - if (!isOldCompressedPairLayout(*p1_sp)) - return {}; + auto [node_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + *table_sp, /*anon_struct_idx=*/1, "__first_node_", "__p1_"); + if (is_compressed_pair) + node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp); - node_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); - if (!node_sp) - return {}; - } - - assert(node_sp); + if (!node_sp) + return {}; return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType(); } @@ -223,19 +218,15 @@ lldb::ValueObjectSP lldb_private::formatters:: llvm::Expected<size_t> lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: CalculateNumChildrenImpl(ValueObject &table) { - if (auto size_sp = table.GetChildMemberWithName("__size_")) + auto [size_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + table, /*anon_struct_idx=*/2, "__size_", "__p2_"); + if (!is_compressed_pair && size_sp) return size_sp->GetValueAsUnsigned(0); - ValueObjectSP p2_sp = table.GetChildMemberWithName("__p2_"); - if (!p2_sp) - return llvm::createStringError( - "Unexpected std::unordered_map layout: __p2_ member not found."); + if (!is_compressed_pair) + return llvm::createStringError("Unsupported std::unordered_map layout."); - if (!isOldCompressedPairLayout(*p2_sp)) - return llvm::createStringError("Unexpected std::unordered_map layout: old " - "__compressed_pair layout not found."); - - ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp); + ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*size_sp); if (!num_elements_sp) return llvm::createStringError( @@ -246,19 +237,13 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd:: } static ValueObjectSP GetTreePointer(ValueObject &table) { - ValueObjectSP tree_sp = table.GetChildMemberWithName("__first_node_"); - if (!tree_sp) { - ValueObjectSP p1_sp = table.GetChildMemberWithName("__p1_"); - if (!p1_sp) - return nullptr; - - if (!isOldCompressedPairLayout(*p1_sp)) - return nullptr; - - tree_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp); - if (!tree_sp) - return nullptr; - } + auto [tree_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + table, /*anon_struct_idx=*/1, "__first_node_", "__p1_"); + if (is_compressed_pair) + tree_sp = GetFirstValueOfLibCXXCompressedPair(*tree_sp); + + if (!tree_sp) + return nullptr; return tree_sp->GetChildMemberWithName("__next_"); } diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp index 4bcdf01..60913e5 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp @@ -126,17 +126,15 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex( } static ValueObjectSP GetDataPointer(ValueObject &root) { - if (auto cap_sp = root.GetChildMemberWithName("__cap_")) - return cap_sp; - - ValueObjectSP cap_sp = root.GetChildMemberWithName("__end_cap_"); + auto [cap_sp, is_compressed_pair] = GetValueOrOldCompressedPair( + root, /*anon_struct_idx=*/2, "__cap_", "__end_cap_"); if (!cap_sp) return nullptr; - if (!isOldCompressedPairLayout(*cap_sp)) - return nullptr; + if (is_compressed_pair) + return GetFirstValueOfLibCXXCompressedPair(*cap_sp); - return GetFirstValueOfLibCXXCompressedPair(*cap_sp); + return cap_sp; } lldb::ChildCacheState diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp index 595e835..f4a695e 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibStdcpp.cpp @@ -241,10 +241,11 @@ VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName(ConstString name) { bool lldb_private::formatters::LibStdcppStringSummaryProvider( ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { ValueObjectSP ptr = valobj.GetChildAtNamePath({"_M_dataplus", "_M_p"}); - if (!ptr) - return false; + if (!ptr || !ptr->GetError().Success()) + stream << "Summary Unavailable"; + else + stream << ptr->GetSummaryAsCString(); - stream << ptr->GetSummaryAsCString(); return true; } diff --git a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp index ef1c2c8..24e8489 100644 --- a/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp +++ b/lldb/source/Plugins/Language/ObjC/NSDictionary.cpp @@ -73,7 +73,8 @@ static CompilerType GetLLDBNSPairType(TargetSP target_sp) { static constexpr llvm::StringLiteral g_lldb_autogen_nspair("__lldb_autogen_nspair"); - compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>(g_lldb_autogen_nspair); + compiler_type = scratch_ts_sp->GetTypeForIdentifier<clang::CXXRecordDecl>( + scratch_ts_sp->getASTContext(), g_lldb_autogen_nspair); if (!compiler_type) { compiler_type = scratch_ts_sp->CreateRecordType( diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp index 24a7371..b1f2a66 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV1.cpp @@ -102,7 +102,7 @@ AppleObjCRuntimeV1::CreateExceptionResolver(const BreakpointSP &bkpt, resolver_sp = std::make_shared<BreakpointResolverName>( bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(), eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, - eLazyBoolNo); + /*offset_is_insn_count = */ false, eLazyBoolNo); // FIXME: don't do catch yet. return resolver_sp; } diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index cca721e..9beb133 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -1163,7 +1163,7 @@ AppleObjCRuntimeV2::CreateExceptionResolver(const BreakpointSP &bkpt, resolver_sp = std::make_shared<BreakpointResolverName>( bkpt, std::get<1>(GetExceptionThrowLocation()).AsCString(), eFunctionNameTypeBase, eLanguageTypeUnknown, Breakpoint::Exact, 0, - eLazyBoolNo); + /*offset_is_insn_count = */ false, eLazyBoolNo); // FIXME: We don't do catch breakpoints for ObjC yet. // Should there be some way for the runtime to specify what it can do in this // regard? diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp index a4b3e26..8dc5f51 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/GNUstepObjCRuntime/GNUstepObjCRuntime.cpp @@ -169,7 +169,8 @@ GNUstepObjCRuntime::CreateExceptionResolver(const BreakpointSP &bkpt, if (throw_bp) resolver_sp = std::make_shared<BreakpointResolverName>( bkpt, "objc_exception_throw", eFunctionNameTypeBase, - eLanguageTypeUnknown, Breakpoint::Exact, 0, eLazyBoolNo); + eLanguageTypeUnknown, Breakpoint::Exact, 0, + /*offset_is_insn_count = */ false, eLazyBoolNo); return resolver_sp; } diff --git a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp index afaaa57..206a471 100644 --- a/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp +++ b/lldb/source/Plugins/MemoryHistory/asan/MemoryHistoryASan.cpp @@ -91,11 +91,9 @@ const char *memory_history_asan_command_format = t; )"; -static void CreateHistoryThreadFromValueObject(ProcessSP process_sp, - ValueObjectSP return_value_sp, - const char *type, - const char *thread_name, - HistoryThreads &result) { +static void CreateHistoryThreadFromValueObject( + ProcessSP process_sp, ValueObjectSP return_value_sp, HistoryPCType pc_type, + const char *type, const char *thread_name, HistoryThreads &result) { std::string count_path = "." + std::string(type) + "_count"; std::string tid_path = "." + std::string(type) + "_tid"; std::string trace_path = "." + std::string(type) + "_trace"; @@ -128,12 +126,8 @@ static void CreateHistoryThreadFromValueObject(ProcessSP process_sp, pcs.push_back(pc); } - // The ASAN runtime already massages the return addresses into call - // addresses, we don't want LLDB's unwinder to try to locate the previous - // instruction again as this might lead to us reporting a different line. - bool pcs_are_call_addresses = true; HistoryThread *history_thread = - new HistoryThread(*process_sp, tid, pcs, pcs_are_call_addresses); + new HistoryThread(*process_sp, tid, pcs, pc_type); ThreadSP new_thread_sp(history_thread); std::ostringstream thread_name_with_number; thread_name_with_number << thread_name << " Thread " << tid; @@ -176,7 +170,8 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) { options.SetAutoApplyFixIts(false); options.SetLanguage(eLanguageTypeObjC_plus_plus); - if (auto m = GetPreferredAsanModule(process_sp->GetTarget())) { + auto [m, pc_type] = GetPreferredAsanModule(process_sp->GetTarget()); + if (m) { SymbolContextList sc_list; sc_list.Append(SymbolContext(std::move(m))); options.SetPreferredSymbolContexts(std::move(sc_list)); @@ -197,10 +192,10 @@ HistoryThreads MemoryHistoryASan::GetHistoryThreads(lldb::addr_t address) { if (!return_value_sp) return result; - CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "free", - "Memory deallocated by", result); - CreateHistoryThreadFromValueObject(process_sp, return_value_sp, "alloc", - "Memory allocated by", result); + CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type, + "free", "Memory deallocated by", result); + CreateHistoryThreadFromValueObject(process_sp, return_value_sp, pc_type, + "alloc", "Memory allocated by", result); return result; } diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index f69358d..931baf5 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -2037,6 +2037,19 @@ static char FindArmAarch64MappingSymbol(const char *symbol_name) { return '\0'; } +static char FindRISCVMappingSymbol(const char *symbol_name) { + if (!symbol_name) + return '\0'; + + if (strcmp(symbol_name, "$d") == 0) { + return 'd'; + } + if (strcmp(symbol_name, "$x") == 0) { + return 'x'; + } + return '\0'; +} + #define STO_MIPS_ISA (3 << 6) #define STO_MICROMIPS (2 << 6) #define IS_MICROMIPS(ST_OTHER) (((ST_OTHER)&STO_MIPS_ISA) == STO_MICROMIPS) @@ -2102,6 +2115,12 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id, if (!symbol_name) symbol_name = ""; + // Skip local symbols starting with ".L" because these are compiler + // generated local labels used for internal purposes (e.g. debugging, + // optimization) and are not relevant for symbol resolution or external + // linkage. + if (llvm::StringRef(symbol_name).starts_with(".L")) + continue; // No need to add non-section symbols that have no names if (symbol.getType() != STT_SECTION && (symbol_name == nullptr || symbol_name[0] == '\0')) @@ -2190,7 +2209,6 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id, int64_t symbol_value_offset = 0; uint32_t additional_flags = 0; - if (arch.IsValid()) { if (arch.GetMachine() == llvm::Triple::arm) { if (symbol.getBinding() == STB_LOCAL) { @@ -2235,6 +2253,27 @@ ObjectFileELF::ParseSymbols(Symtab *symtab, user_id_t start_id, if (mapping_symbol) continue; } + } else if (arch.GetTriple().isRISCV()) { + if (symbol.getBinding() == STB_LOCAL) { + char mapping_symbol = FindRISCVMappingSymbol(symbol_name); + if (symbol_type == eSymbolTypeCode) { + // Only handle $d and $x mapping symbols. + // Other mapping symbols are ignored as they don't affect address + // classification. + switch (mapping_symbol) { + case 'x': + // $x - marks a RISCV instruction sequence + address_class_map[symbol.st_value] = AddressClass::eCode; + break; + case 'd': + // $d - marks a RISCV data item sequence + address_class_map[symbol.st_value] = AddressClass::eData; + break; + } + } + if (mapping_symbol) + continue; + } } if (arch.GetMachine() == llvm::Triple::arm) { diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 13df6e2..924e340 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -1156,6 +1156,7 @@ AddressClass ObjectFileMachO::GetAddressClass(lldb::addr_t file_addr) { case eSectionTypeDataObjCMessageRefs: case eSectionTypeDataObjCCFStrings: case eSectionTypeGoSymtab: + case eSectionTypeWasmName: return AddressClass::eData; case eSectionTypeDebug: @@ -2784,7 +2785,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { const char *symbol_name_non_abi_mangled = NULL; SectionSP symbol_section; - uint32_t symbol_byte_size = 0; bool add_nlist = true; bool is_debug = ((nlist.n_type & N_STAB) != 0); bool demangled_is_synthesized = false; @@ -3436,61 +3436,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { if (symbol_section) { const addr_t section_file_addr = symbol_section->GetFileAddress(); - if (symbol_byte_size == 0 && - function_starts_count > 0) { - addr_t symbol_lookup_file_addr = nlist.n_value; - // Do an exact address match for non-ARM addresses, - // else get the closest since the symbol might be a - // thumb symbol which has an address with bit zero - // set - FunctionStarts::Entry *func_start_entry = - function_starts.FindEntry(symbol_lookup_file_addr, - !is_arm); - if (is_arm && func_start_entry) { - // Verify that the function start address is the - // symbol address (ARM) or the symbol address + 1 - // (thumb) - if (func_start_entry->addr != - symbol_lookup_file_addr && - func_start_entry->addr != - (symbol_lookup_file_addr + 1)) { - // Not the right entry, NULL it out... - func_start_entry = NULL; - } - } - if (func_start_entry) { - func_start_entry->data = true; - - addr_t symbol_file_addr = func_start_entry->addr; - uint32_t symbol_flags = 0; - if (is_arm) { - if (symbol_file_addr & 1) - symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB; - symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - } - - const FunctionStarts::Entry *next_func_start_entry = - function_starts.FindNextEntry(func_start_entry); - const addr_t section_end_file_addr = - section_file_addr + - symbol_section->GetByteSize(); - if (next_func_start_entry) { - addr_t next_symbol_file_addr = - next_func_start_entry->addr; - // Be sure the clear the Thumb address bit when - // we calculate the size from the current and - // next address - if (is_arm) - next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - symbol_byte_size = std::min<lldb::addr_t>( - next_symbol_file_addr - symbol_file_addr, - section_end_file_addr - symbol_file_addr); - } else { - symbol_byte_size = - section_end_file_addr - symbol_file_addr; - } - } - } symbol_value -= section_file_addr; } @@ -3619,9 +3564,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { } sym[sym_idx].SetFlags(nlist.n_type << 16 | nlist.n_desc); - if (symbol_byte_size > 0) - sym[sym_idx].SetByteSize(symbol_byte_size); - if (demangled_is_synthesized) sym[sym_idx].SetDemangledNameIsSynthesized(true); ++sym_idx; @@ -3710,7 +3652,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { SymbolType type = eSymbolTypeInvalid; SectionSP symbol_section; - lldb::addr_t symbol_byte_size = 0; bool add_nlist = true; bool is_gsym = false; bool demangled_is_synthesized = false; @@ -4296,47 +4237,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { if (symbol_section) { const addr_t section_file_addr = symbol_section->GetFileAddress(); - if (symbol_byte_size == 0 && function_starts_count > 0) { - addr_t symbol_lookup_file_addr = nlist.n_value; - // Do an exact address match for non-ARM addresses, else get the - // closest since the symbol might be a thumb symbol which has an - // address with bit zero set. - FunctionStarts::Entry *func_start_entry = - function_starts.FindEntry(symbol_lookup_file_addr, !is_arm); - if (is_arm && func_start_entry) { - // Verify that the function start address is the symbol address - // (ARM) or the symbol address + 1 (thumb). - if (func_start_entry->addr != symbol_lookup_file_addr && - func_start_entry->addr != (symbol_lookup_file_addr + 1)) { - // Not the right entry, NULL it out... - func_start_entry = nullptr; - } - } - if (func_start_entry) { - func_start_entry->data = true; - - addr_t symbol_file_addr = func_start_entry->addr; - if (is_arm) - symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - - const FunctionStarts::Entry *next_func_start_entry = - function_starts.FindNextEntry(func_start_entry); - const addr_t section_end_file_addr = - section_file_addr + symbol_section->GetByteSize(); - if (next_func_start_entry) { - addr_t next_symbol_file_addr = next_func_start_entry->addr; - // Be sure the clear the Thumb address bit when we calculate the - // size from the current and next address - if (is_arm) - next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - symbol_byte_size = std::min<lldb::addr_t>( - next_symbol_file_addr - symbol_file_addr, - section_end_file_addr - symbol_file_addr); - } else { - symbol_byte_size = section_end_file_addr - symbol_file_addr; - } - } - } symbol_value -= section_file_addr; } @@ -4443,9 +4343,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { if (nlist.n_desc & N_WEAK_REF) sym[sym_idx].SetIsWeak(true); - if (symbol_byte_size > 0) - sym[sym_idx].SetByteSize(symbol_byte_size); - if (demangled_is_synthesized) sym[sym_idx].SetDemangledNameIsSynthesized(true); @@ -4564,23 +4461,7 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { Address symbol_addr; if (module_sp->ResolveFileAddress(symbol_file_addr, symbol_addr)) { SectionSP symbol_section(symbol_addr.GetSection()); - uint32_t symbol_byte_size = 0; if (symbol_section) { - const addr_t section_file_addr = symbol_section->GetFileAddress(); - const FunctionStarts::Entry *next_func_start_entry = - function_starts.FindNextEntry(func_start_entry); - const addr_t section_end_file_addr = - section_file_addr + symbol_section->GetByteSize(); - if (next_func_start_entry) { - addr_t next_symbol_file_addr = next_func_start_entry->addr; - if (is_arm) - next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK; - symbol_byte_size = std::min<lldb::addr_t>( - next_symbol_file_addr - symbol_file_addr, - section_end_file_addr - symbol_file_addr); - } else { - symbol_byte_size = section_end_file_addr - symbol_file_addr; - } sym[sym_idx].SetID(synthetic_sym_id++); // Don't set the name for any synthetic symbols, the Symbol // object will generate one if needed when the name is accessed @@ -4592,8 +4473,6 @@ void ObjectFileMachO::ParseSymtab(Symtab &symtab) { add_symbol_addr(symbol_addr.GetFileAddress()); if (symbol_flags) sym[sym_idx].SetFlags(symbol_flags); - if (symbol_byte_size) - sym[sym_idx].SetByteSize(symbol_byte_size); ++sym_idx; } } diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp index 25e9888..c361087 100644 --- a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -308,40 +308,49 @@ Status MinidumpFileBuilder::AddModuleList() { // the llvm::minidump::Module's structures into helper data size_t size_before = GetCurrentDataEndOffset(); - // This is the size of the main part of the ModuleList stream. - // It consists of a module number and corresponding number of - // structs describing individual modules - size_t module_stream_size = - sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size; - - // Adding directory describing this stream. - error = AddDirectory(StreamType::ModuleList, module_stream_size); - if (error.Fail()) - return error; - - m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t)); - // Temporary storage for the helper data (of variable length) // as these cannot be dumped to m_data before dumping entire // array of module structures. DataBufferHeap helper_data; + // Vector to store modules that pass validation. + std::vector<std::pair<ModuleSP, uint64_t>> valid_modules; + for (size_t i = 0; i < modules_count; ++i) { ModuleSP mod = modules.GetModuleAtIndex(i); std::string module_name = mod->GetSpecificationDescription(); auto maybe_mod_size = getModuleFileSize(target, mod); if (!maybe_mod_size) { llvm::Error mod_size_err = maybe_mod_size.takeError(); - llvm::handleAllErrors(std::move(mod_size_err), - [&](const llvm::ErrorInfoBase &E) { - error = Status::FromErrorStringWithFormat( - "Unable to get the size of module %s: %s.", - module_name.c_str(), E.message().c_str()); - }); - return error; + Log *log = GetLog(LLDBLog::Object); + llvm::handleAllErrors( + std::move(mod_size_err), [&](const llvm::ErrorInfoBase &E) { + if (log) { + LLDB_LOGF(log, "Unable to get the size of module %s: %s", + module_name.c_str(), E.message().c_str()); + } + }); + continue; } + valid_modules.emplace_back(mod, *maybe_mod_size); + } + + size_t module_stream_size = sizeof(llvm::support::ulittle32_t) + + valid_modules.size() * minidump_module_size; + + error = AddDirectory(StreamType::ModuleList, module_stream_size); + if (error.Fail()) + return error; - uint64_t mod_size = std::move(*maybe_mod_size); + // Setting the header with the number of modules. + llvm::support::ulittle32_t count = + static_cast<llvm::support::ulittle32_t>(valid_modules.size()); + m_data.AppendData(&count, sizeof(llvm::support::ulittle32_t)); + + for (const auto &valid_module : valid_modules) { + ModuleSP mod = valid_module.first; + uint64_t module_size = valid_module.second; + std::string module_name = mod->GetSpecificationDescription(); llvm::support::ulittle32_t signature = static_cast<llvm::support::ulittle32_t>( @@ -381,7 +390,7 @@ Status MinidumpFileBuilder::AddModuleList() { llvm::minidump::Module m{}; m.BaseOfImage = static_cast<llvm::support::ulittle64_t>( mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target)); - m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size); + m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(module_size); m.Checksum = static_cast<llvm::support::ulittle32_t>(0); m.TimeDateStamp = static_cast<llvm::support::ulittle32_t>(std::time(nullptr)); diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp index b1efd25..492b441 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/BinaryFormat/Wasm.h" +#include "llvm/Support/CheckedArithmetic.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include <optional> @@ -35,6 +36,82 @@ LLDB_PLUGIN_DEFINE(ObjectFileWasm) static const uint32_t kWasmHeaderSize = sizeof(llvm::wasm::WasmMagic) + sizeof(llvm::wasm::WasmVersion); +/// Helper to read a 32-bit ULEB using LLDB's DataExtractor. +static inline llvm::Expected<uint32_t> GetULEB32(DataExtractor &data, + lldb::offset_t &offset) { + const uint64_t value = data.GetULEB128(&offset); + if (value > std::numeric_limits<uint32_t>::max()) + return llvm::createStringError("ULEB exceeds 32 bits"); + return value; +} + +/// Helper to read a 32-bit ULEB using LLVM's DataExtractor. +static inline llvm::Expected<uint32_t> +GetULEB32(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) { + const uint64_t value = data.getULEB128(c); + if (!c) + return c.takeError(); + if (value > std::numeric_limits<uint32_t>::max()) + return llvm::createStringError("ULEB exceeds 32 bits"); + return value; +} + +/// Helper to read a Wasm string, whcih is encoded as a vector of UTF-8 codes. +static inline llvm::Expected<std::string> +GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) { + llvm::Expected<uint32_t> len = GetULEB32(data, c); + if (!len) + return len.takeError(); + + llvm::SmallVector<uint8_t, 32> str_storage; + data.getU8(c, str_storage, *len); + if (!c) + return c.takeError(); + + return std::string(toStringRef(llvm::ArrayRef(str_storage))); +} + +/// An "init expr" refers to a constant expression used to determine the initial +/// value of certain elements within a module during instantiation. These +/// expressions are restricted to operations that can be evaluated at module +/// instantiation time. Currently we only support simple constant opcodes. +static lldb::offset_t GetWasmOffsetFromInitExpr(DataExtractor &data, + lldb::offset_t &offset) { + lldb::offset_t init_expr_offset = LLDB_INVALID_OFFSET; + + uint8_t opcode = data.GetU8(&offset); + switch (opcode) { + case llvm::wasm::WASM_OPCODE_I32_CONST: + case llvm::wasm::WASM_OPCODE_I64_CONST: + init_expr_offset = data.GetSLEB128(&offset); + break; + case llvm::wasm::WASM_OPCODE_GLOBAL_GET: + init_expr_offset = data.GetULEB128(&offset); + break; + case llvm::wasm::WASM_OPCODE_F32_CONST: + case llvm::wasm::WASM_OPCODE_F64_CONST: + // Not a meaningful offset. + data.GetFloat(&offset); + break; + case llvm::wasm::WASM_OPCODE_REF_NULL: + // Not a meaningful offset. + data.GetULEB128(&offset); + break; + } + + // Make sure the opcodes we read aren't part of an extended init expr. + opcode = data.GetU8(&offset); + if (opcode == llvm::wasm::WASM_OPCODE_END) + return init_expr_offset; + + // Extended init expressions are not supported, but we still have to parse + // them to skip over them and read the next segment. + do { + opcode = data.GetU8(&offset); + } while (opcode != llvm::wasm::WASM_OPCODE_END); + return LLDB_INVALID_OFFSET; +} + /// Checks whether the data buffer starts with a valid Wasm module header. static bool ValidateModuleHeader(const DataBufferSP &data_sp) { if (!data_sp || data_sp->GetByteSize() < kWasmHeaderSize) @@ -50,32 +127,6 @@ static bool ValidateModuleHeader(const DataBufferSP &data_sp) { return version == llvm::wasm::WasmVersion; } -static std::optional<ConstString> -GetWasmString(llvm::DataExtractor &data, llvm::DataExtractor::Cursor &c) { - // A Wasm string is encoded as a vector of UTF-8 codes. - // Vectors are encoded with their u32 length followed by the element - // sequence. - uint64_t len = data.getULEB128(c); - if (!c) { - consumeError(c.takeError()); - return std::nullopt; - } - - if (len >= (uint64_t(1) << 32)) { - return std::nullopt; - } - - llvm::SmallVector<uint8_t, 32> str_storage; - data.getU8(c, str_storage, len); - if (!c) { - consumeError(c.takeError()); - return std::nullopt; - } - - llvm::StringRef str = toStringRef(llvm::ArrayRef(str_storage)); - return ConstString(str); -} - char ObjectFileWasm::ID; void ObjectFileWasm::Initialize() { @@ -174,7 +225,7 @@ bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) { if (!c) return !llvm::errorToBool(c.takeError()); - if (payload_len >= (uint64_t(1) << 32)) + if (payload_len > std::numeric_limits<uint32_t>::max()) return false; if (section_id == llvm::wasm::WASM_SEC_CUSTOM) { @@ -182,16 +233,19 @@ bool ObjectFileWasm::DecodeNextSection(lldb::offset_t *offset_ptr) { // identifying the custom section, followed by an uninterpreted sequence // of bytes. lldb::offset_t prev_offset = c.tell(); - std::optional<ConstString> sect_name = GetWasmString(data, c); - if (!sect_name) + llvm::Expected<std::string> sect_name = GetWasmString(data, c); + if (!sect_name) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Object), sect_name.takeError(), + "failed to parse section name: {0}"); return false; + } if (payload_len < c.tell() - prev_offset) return false; uint32_t section_length = payload_len - (c.tell() - prev_offset); m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), section_length, - section_id, *sect_name}); + section_id, ConstString(*sect_name)}); *offset_ptr += (c.tell() + section_length); } else if (section_id <= llvm::wasm::WASM_SEC_LAST_KNOWN) { m_sect_infos.push_back(section_info{*offset_ptr + c.tell(), @@ -248,16 +302,221 @@ bool ObjectFileWasm::ParseHeader() { return true; } -void ObjectFileWasm::ParseSymtab(Symtab &symtab) {} +struct WasmFunction { + lldb::offset_t section_offset = LLDB_INVALID_OFFSET; + uint32_t size = 0; +}; + +static llvm::Expected<std::vector<WasmFunction>> +ParseFunctions(DataExtractor &data) { + lldb::offset_t offset = 0; + + llvm::Expected<uint32_t> function_count = GetULEB32(data, offset); + if (!function_count) + return function_count.takeError(); + + std::vector<WasmFunction> functions; + functions.reserve(*function_count); + + for (uint32_t i = 0; i < *function_count; ++i) { + llvm::Expected<uint32_t> function_size = GetULEB32(data, offset); + if (!function_size) + return function_size.takeError(); + // llvm-objdump considers the ULEB with the function size to be part of the + // function. We can't do that here because that would break symbolic + // breakpoints, as that address is never executed. + functions.push_back({offset, *function_size}); + + std::optional<lldb::offset_t> next_offset = + llvm::checkedAddUnsigned<lldb::offset_t>(offset, *function_size); + if (!next_offset) + return llvm::createStringError("function offset overflows 64 bits"); + offset = *next_offset; + } + + return functions; +} + +struct WasmSegment { + enum SegmentType { + Active, + Passive, + }; + + std::string name; + SegmentType type = Passive; + lldb::offset_t section_offset = LLDB_INVALID_OFFSET; + uint32_t size = 0; + uint32_t memory_index = 0; + lldb::offset_t init_expr_offset = 0; + + lldb::offset_t GetFileOffset() const { return section_offset & 0xffffffff; } +}; + +static llvm::Expected<std::vector<WasmSegment>> ParseData(DataExtractor &data) { + lldb::offset_t offset = 0; + + llvm::Expected<uint32_t> segment_count = GetULEB32(data, offset); + if (!segment_count) + return segment_count.takeError(); + + std::vector<WasmSegment> segments; + segments.reserve(*segment_count); + + for (uint32_t i = 0; i < *segment_count; ++i) { + llvm::Expected<uint32_t> flags = GetULEB32(data, offset); + if (!flags) + return flags.takeError(); + + WasmSegment segment; + + // Data segments have a mode that identifies them as either passive or + // active. An active data segment copies its contents into a memory during + // instantiation, as specified by a memory index and a constant expression + // defining an offset into that memory. + segment.type = (*flags & llvm::wasm::WASM_DATA_SEGMENT_IS_PASSIVE) + ? WasmSegment::Passive + : WasmSegment::Active; + + if (*flags & llvm::wasm::WASM_DATA_SEGMENT_HAS_MEMINDEX) { + assert(segment.type == WasmSegment::Active); + llvm::Expected<uint32_t> memidx = GetULEB32(data, offset); + if (!memidx) + return memidx.takeError(); + segment.memory_index = *memidx; + } + + if (segment.type == WasmSegment::Active) + segment.init_expr_offset = GetWasmOffsetFromInitExpr(data, offset); + + llvm::Expected<uint32_t> segment_size = GetULEB32(data, offset); + if (!segment_size) + return segment_size.takeError(); + + segment.section_offset = offset; + segment.size = *segment_size; + segments.push_back(segment); + + std::optional<lldb::offset_t> next_offset = + llvm::checkedAddUnsigned<lldb::offset_t>(offset, *segment_size); + if (!next_offset) + return llvm::createStringError("segment offset overflows 64 bits"); + offset = *next_offset; + } + + return segments; +} + +static llvm::Expected<std::vector<Symbol>> +ParseNames(SectionSP code_section_sp, DataExtractor &name_data, + const std::vector<WasmFunction> &functions, + std::vector<WasmSegment> &segments) { + + llvm::DataExtractor data = name_data.GetAsLLVM(); + llvm::DataExtractor::Cursor c(0); + std::vector<Symbol> symbols; + while (c && c.tell() < data.size()) { + const uint8_t type = data.getU8(c); + llvm::Expected<uint32_t> size = GetULEB32(data, c); + if (!size) + return size.takeError(); + + switch (type) { + case llvm::wasm::WASM_NAMES_FUNCTION: { + const uint64_t count = data.getULEB128(c); + if (count > std::numeric_limits<uint32_t>::max()) + return llvm::createStringError("function count overflows uint32_t"); + + for (uint64_t i = 0; c && i < count; ++i) { + llvm::Expected<uint32_t> idx = GetULEB32(data, c); + if (!idx) + return idx.takeError(); + llvm::Expected<std::string> name = GetWasmString(data, c); + if (!name) + return name.takeError(); + if (*idx >= functions.size()) + continue; + symbols.emplace_back( + symbols.size(), *name, lldb::eSymbolTypeCode, + /*external=*/false, /*is_debug=*/false, /*is_trampoline=*/false, + /*is_artificial=*/false, code_section_sp, + functions[i].section_offset, functions[i].size, + /*size_is_valid=*/true, /*contains_linker_annotations=*/false, + /*flags=*/0); + } + } break; + case llvm::wasm::WASM_NAMES_DATA_SEGMENT: { + llvm::Expected<uint32_t> count = GetULEB32(data, c); + if (!count) + return count.takeError(); + for (uint32_t i = 0; c && i < *count; ++i) { + llvm::Expected<uint32_t> idx = GetULEB32(data, c); + if (!idx) + return idx.takeError(); + llvm::Expected<std::string> name = GetWasmString(data, c); + if (!name) + return name.takeError(); + if (*idx >= segments.size()) + continue; + // Update the segment name. + segments[i].name = *name; + } + + } break; + case llvm::wasm::WASM_NAMES_GLOBAL: + case llvm::wasm::WASM_NAMES_LOCAL: + default: + std::optional<lldb::offset_t> offset = + llvm::checkedAddUnsigned<lldb::offset_t>(c.tell(), *size); + if (!offset) + return llvm::createStringError("offset overflows 64 bits"); + c.seek(*offset); + } + } + + if (!c) + return c.takeError(); + + return symbols; +} + +void ObjectFileWasm::ParseSymtab(Symtab &symtab) { + for (const Symbol &symbol : m_symbols) + symtab.AddSymbol(symbol); + + symtab.Finalize(); + m_symbols.clear(); +} static SectionType GetSectionTypeFromName(llvm::StringRef Name) { - if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_")) { + if (Name == "name") + return lldb::eSectionTypeWasmName; + if (Name.consume_front(".debug_") || Name.consume_front(".zdebug_")) return ObjectFile::GetDWARFSectionTypeFromName(Name); - } return eSectionTypeOther; } +std::optional<ObjectFileWasm::section_info> +ObjectFileWasm::GetSectionInfo(uint32_t section_id) { + for (const section_info §_info : m_sect_infos) { + if (sect_info.id == section_id) + return sect_info; + } + return std::nullopt; +} + +std::optional<ObjectFileWasm::section_info> +ObjectFileWasm::GetSectionInfo(llvm::StringRef section_name) { + for (const section_info §_info : m_sect_infos) { + if (sect_info.name == section_name) + return sect_info; + } + return std::nullopt; +} + void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { + Log *log = GetLog(LLDBLog::Object); + if (m_sections_up) return; @@ -271,7 +530,7 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { SectionType section_type = eSectionTypeOther; ConstString section_name; offset_t file_offset = sect_info.offset & 0xffffffff; - addr_t vm_addr = file_offset; + addr_t vm_addr = sect_info.offset; size_t vm_size = sect_info.size; if (llvm::wasm::WASM_SEC_CODE == sect_info.id) { @@ -294,23 +553,107 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) { } } - SectionSP section_sp( - new Section(GetModule(), // Module to which this section belongs. - this, // ObjectFile to which this section belongs and - // should read section data from. - section_type, // Section ID. - section_name, // Section name. - section_type, // Section type. - vm_addr, // VM address. - vm_size, // VM size in bytes of this section. - file_offset, // Offset of this section in the file. - sect_info.size, // Size of the section as found in the file. - 0, // Alignment of the section - 0, // Flags for this section. - 1)); // Number of host bytes per target byte + SectionSP section_sp = std::make_shared<Section>( + GetModule(), // Module to which this section belongs. + this, // ObjectFile to which this section belongs and + // should read section data from. + section_type, // Section ID. + section_name, // Section name. + section_type, // Section type. + vm_addr, // VM address. + vm_size, // VM size in bytes of this section. + file_offset, // Offset of this section in the file. + sect_info.size, // Size of the section as found in the file. + 0, // Alignment of the section + 0, // Flags for this section. + 1); // Number of host bytes per target byte m_sections_up->AddSection(section_sp); unified_section_list.AddSection(section_sp); } + + // The name section contains names and indexes. First parse the data from the + // relevant sections so we can access it by its index. + std::vector<WasmFunction> functions; + std::vector<WasmSegment> segments; + + // Parse the code section. + if (std::optional<section_info> info = + GetSectionInfo(llvm::wasm::WASM_SEC_CODE)) { + DataExtractor code_data = ReadImageData(info->offset, info->size); + llvm::Expected<std::vector<WasmFunction>> maybe_functions = + ParseFunctions(code_data); + if (!maybe_functions) { + LLDB_LOG_ERROR(log, maybe_functions.takeError(), + "Failed to parse Wasm code section: {0}"); + } else { + functions = *maybe_functions; + } + } + + // Parse the data section. + std::optional<section_info> data_info = + GetSectionInfo(llvm::wasm::WASM_SEC_DATA); + if (data_info) { + DataExtractor data_data = ReadImageData(data_info->offset, data_info->size); + llvm::Expected<std::vector<WasmSegment>> maybe_segments = + ParseData(data_data); + if (!maybe_segments) { + LLDB_LOG_ERROR(log, maybe_segments.takeError(), + "Failed to parse Wasm data section: {0}"); + } else { + segments = *maybe_segments; + } + } + + if (std::optional<section_info> info = GetSectionInfo("name")) { + DataExtractor names_data = ReadImageData(info->offset, info->size); + llvm::Expected<std::vector<Symbol>> symbols = ParseNames( + m_sections_up->FindSectionByType(lldb::eSectionTypeCode, false), + names_data, functions, segments); + if (!symbols) { + LLDB_LOG_ERROR(log, symbols.takeError(), + "Failed to parse Wasm names: {0}"); + } else { + m_symbols = *symbols; + } + } + + lldb::user_id_t segment_id = 0; + for (const WasmSegment &segment : segments) { + if (segment.type == WasmSegment::Active) { + // FIXME: Support segments with a memory index. + if (segment.memory_index != 0) { + LLDB_LOG(log, "Skipping segment {0}: non-zero memory index is " + "currently unsupported"); + continue; + } + + if (segment.init_expr_offset == LLDB_INVALID_OFFSET) { + LLDB_LOG(log, "Skipping segment {0}: unsupported init expression"); + continue; + } + } + + const lldb::addr_t file_vm_addr = + segment.type == WasmSegment::Active + ? segment.init_expr_offset + : data_info->offset + segment.section_offset; + const lldb::offset_t file_offset = + data_info->GetFileOffset() + segment.GetFileOffset(); + SectionSP segment_sp = std::make_shared<Section>( + GetModule(), + /*obj_file=*/this, + ++segment_id << 8, // 1-based segment index, shifted by 8 bits to avoid + // collision with section IDs. + ConstString(segment.name), eSectionTypeData, + /*file_vm_addr=*/file_vm_addr, + /*vm_size=*/segment.size, + /*file_offset=*/file_offset, + /*file_size=*/segment.size, + /*log2align=*/0, /*flags=*/0); + m_sections_up->AddSection(segment_sp); + GetModule()->GetSectionList()->AddSection(segment_sp); + } } bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address, @@ -395,11 +738,15 @@ std::optional<FileSpec> ObjectFileWasm::GetExternalDebugInfoFileSpec() { const uint32_t kBufferSize = 1024; DataExtractor section_header_data = ReadImageData(sect_info.offset, kBufferSize); + llvm::DataExtractor data = section_header_data.GetAsLLVM(); llvm::DataExtractor::Cursor c(0); - std::optional<ConstString> symbols_url = GetWasmString(data, c); - if (symbols_url) - return FileSpec(symbols_url->GetStringRef()); + llvm::Expected<std::string> symbols_url = GetWasmString(data, c); + if (!symbols_url) { + llvm::consumeError(symbols_url.takeError()); + return std::nullopt; + } + return FileSpec(*symbols_url); } } return std::nullopt; @@ -431,7 +778,7 @@ void ObjectFileWasm::Dump(Stream *s) { } void ObjectFileWasm::DumpSectionHeader(llvm::raw_ostream &ostream, - const section_info_t &sh) { + const section_info &sh) { ostream << llvm::left_justify(sh.name.GetStringRef(), 16) << " " << llvm::format_hex(sh.offset, 10) << " " << llvm::format_hex(sh.size, 10) << " " << llvm::format_hex(sh.id, 6) diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h index 531b5f0..86ecbf2 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h @@ -128,20 +128,25 @@ private: /// Read a range of bytes from the Wasm module. DataExtractor ReadImageData(lldb::offset_t offset, uint32_t size); - typedef struct section_info { + struct section_info { lldb::offset_t offset; uint32_t size; uint32_t id; ConstString name; - } section_info_t; + lldb::offset_t GetFileOffset() const { return offset & 0xffffffff; } + }; + + std::optional<section_info> GetSectionInfo(uint32_t section_id); + std::optional<section_info> GetSectionInfo(llvm::StringRef section_name); /// Wasm section header dump routines. /// \{ - void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info_t &sh); + void DumpSectionHeader(llvm::raw_ostream &ostream, const section_info &sh); void DumpSectionHeaders(llvm::raw_ostream &ostream); /// \} - std::vector<section_info_t> m_sect_infos; + std::vector<section_info> m_sect_infos; + std::vector<Symbol> m_symbols; ArchSpec m_arch; UUID m_uuid; }; diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp index 1db7bc7..cd72454 100644 --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp @@ -1039,7 +1039,12 @@ ResolveSDKPathFromDebugInfo(lldb_private::Target *target) { SymbolFile *sym_file = exe_module_sp->GetSymbolFile(); if (!sym_file) - return llvm::createStringError("Failed to get symbol file from module"); + return llvm::createStringError("Failed to get symbol file from executable"); + + if (sym_file->GetNumCompileUnits() == 0) + return llvm::createStringError( + "Failed to resolve SDK for target: executable's symbol file has no " + "compile units"); XcodeSDK merged_sdk; for (unsigned i = 0; i < sym_file->GetNumCompileUnits(); ++i) { @@ -1397,6 +1402,12 @@ PlatformDarwin::GetSDKPathFromDebugInfo(Module &module) { llvm::formatv("No symbol file available for module '{0}'", module.GetFileSpec().GetFilename().AsCString(""))); + if (sym_file->GetNumCompileUnits() == 0) + return llvm::createStringError( + llvm::formatv("Could not resolve SDK for module '{0}'. Symbol file has " + "no compile units.", + module.GetFileSpec())); + bool found_public_sdk = false; bool found_internal_sdk = false; XcodeSDK merged_sdk; diff --git a/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt b/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt index b0d1370..c35b4de 100644 --- a/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt +++ b/lldb/source/Plugins/Process/FreeBSDKernel/CMakeLists.txt @@ -2,7 +2,7 @@ set(FBSDKERNEL_LIBS) if(FBSDVMCore_FOUND) list(APPEND FBSDKERNEL_LIBS fbsdvmcore) endif() -if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") +if("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") list(APPEND FBSDKERNEL_LIBS kvm) endif() diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp index fdafacf..c1bc6a3 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.cpp @@ -76,7 +76,7 @@ NativeRegisterContextLinux_arm::NativeRegisterContextLinux_arm( ::memset(&m_fpr, 0, sizeof(m_fpr)); ::memset(&m_gpr_arm, 0, sizeof(m_gpr_arm)); ::memset(&m_hwp_regs, 0, sizeof(m_hwp_regs)); - ::memset(&m_hbr_regs, 0, sizeof(m_hbr_regs)); + ::memset(&m_hbp_regs, 0, sizeof(m_hbp_regs)); // 16 is just a maximum value, query hardware for actual watchpoint count m_max_hwp_supported = 16; @@ -283,522 +283,43 @@ bool NativeRegisterContextLinux_arm::IsFPR(unsigned reg) const { return false; } -uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareBreakpoints() { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - Status error; - - // Read hardware breakpoint and watchpoint information. - error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return 0; - - LLDB_LOG(log, "{0}", m_max_hbp_supported); - return m_max_hbp_supported; -} - -uint32_t -NativeRegisterContextLinux_arm::SetHardwareBreakpoint(lldb::addr_t addr, - size_t size) { - Log *log = GetLog(POSIXLog::Breakpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x}", addr, size); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return LLDB_INVALID_INDEX32; - - uint32_t control_value = 0, bp_index = 0; - - // Setup address and control values. - // Use size to get a hint of arm vs thumb modes. - switch (size) { - case 2: - control_value = (0x3 << 5) | 7; - addr &= ~1; - break; - case 4: - control_value = (0xfu << 5) | 7; - addr &= ~3; - break; - default: - return LLDB_INVALID_INDEX32; - } - - // Iterate over stored breakpoints and find a free bp_index - bp_index = LLDB_INVALID_INDEX32; - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if ((m_hbr_regs[i].control & 1) == 0) { - bp_index = i; // Mark last free slot - } else if (m_hbr_regs[i].address == addr) { - return LLDB_INVALID_INDEX32; // We do not support duplicate breakpoints. - } - } - - if (bp_index == LLDB_INVALID_INDEX32) - return LLDB_INVALID_INDEX32; - - // Update breakpoint in local cache - m_hbr_regs[bp_index].real_addr = addr; - m_hbr_regs[bp_index].address = addr; - m_hbr_regs[bp_index].control = control_value; - - // PTRACE call to set corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, - bp_index); - - if (error.Fail()) { - m_hbr_regs[bp_index].address = 0; - m_hbr_regs[bp_index].control &= ~1; - - return LLDB_INVALID_INDEX32; - } - - return bp_index; -} - -bool NativeRegisterContextLinux_arm::ClearHardwareBreakpoint(uint32_t hw_idx) { - Log *log = GetLog(POSIXLog::Breakpoints); - LLDB_LOG(log, "hw_idx: {0}", hw_idx); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return false; - - if (hw_idx >= m_max_hbp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hbr_regs[hw_idx].address; - uint32_t tempControl = m_hbr_regs[hw_idx].control; - - m_hbr_regs[hw_idx].control &= ~1; - m_hbr_regs[hw_idx].address = 0; - - // PTRACE call to clear corresponding hardware breakpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, - hw_idx); - - if (error.Fail()) { - m_hbr_regs[hw_idx].control = tempControl; - m_hbr_regs[hw_idx].address = tempAddr; - - return false; - } - - return true; -} - -Status NativeRegisterContextLinux_arm::GetHardwareBreakHitIndex( - uint32_t &bp_index, lldb::addr_t trap_addr) { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - lldb::addr_t break_addr; - - for (bp_index = 0; bp_index < m_max_hbp_supported; ++bp_index) { - break_addr = m_hbr_regs[bp_index].address; - - if ((m_hbr_regs[bp_index].control & 0x1) && (trap_addr == break_addr)) { - m_hbr_regs[bp_index].hit_addr = trap_addr; - return Status(); - } - } - - bp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -Status NativeRegisterContextLinux_arm::ClearAllHardwareBreakpoints() { - Log *log = GetLog(POSIXLog::Breakpoints); - - LLDB_LOGF(log, "NativeRegisterContextLinux_arm::%s()", __FUNCTION__); - - Status error; - - // Read hardware breakpoint and watchpoint information. - error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return error; - - lldb::addr_t tempAddr = 0; - uint32_t tempControl = 0; - - for (uint32_t i = 0; i < m_max_hbp_supported; i++) { - if (m_hbr_regs[i].control & 0x01) { - // Create a backup we can revert to in case of failure. - tempAddr = m_hbr_regs[i].address; - tempControl = m_hbr_regs[i].control; - - // Clear breakpoints in local cache - m_hbr_regs[i].control &= ~1; - m_hbr_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = - WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeBREAK, i); - - if (error.Fail()) { - m_hbr_regs[i].control = tempControl; - m_hbr_regs[i].address = tempAddr; - - return error; - } - } - } - - return Status(); -} - -uint32_t NativeRegisterContextLinux_arm::NumSupportedHardwareWatchpoints() { - Log *log = GetLog(POSIXLog::Watchpoints); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return 0; - - LLDB_LOG(log, "{0}", m_max_hwp_supported); - return m_max_hwp_supported; -} - -uint32_t NativeRegisterContextLinux_arm::SetHardwareWatchpoint( - lldb::addr_t addr, size_t size, uint32_t watch_flags) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "addr: {0:x}, size: {1:x} watch_flags: {2:x}", addr, size, - watch_flags); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return LLDB_INVALID_INDEX32; - - uint32_t control_value = 0, wp_index = 0, addr_word_offset = 0, byte_mask = 0; - lldb::addr_t real_addr = addr; - - // Check if we are setting watchpoint other than read/write/access Also - // update watchpoint flag to match Arm write-read bit configuration. - switch (watch_flags) { - case 1: - watch_flags = 2; - break; - case 2: - watch_flags = 1; - break; - case 3: - break; - default: - return LLDB_INVALID_INDEX32; - } - - // Can't watch zero bytes - // Can't watch more than 4 bytes per WVR/WCR pair - - if (size == 0 || size > 4) - return LLDB_INVALID_INDEX32; - - // Check 4-byte alignment for hardware watchpoint target address. Below is a - // hack to recalculate address and size in order to make sure we can watch - // non 4-byte aligned addresses as well. - if (addr & 0x03) { - uint8_t watch_mask = (addr & 0x03) + size; - - if (watch_mask > 0x04) - return LLDB_INVALID_INDEX32; - else if (watch_mask <= 0x02) - size = 2; - else - size = 4; - - addr = addr & (~0x03); - } - - // We can only watch up to four bytes that follow a 4 byte aligned address - // per watchpoint register pair, so make sure we can properly encode this. - addr_word_offset = addr % 4; - byte_mask = ((1u << size) - 1u) << addr_word_offset; - - // Check if we need multiple watchpoint register - if (byte_mask > 0xfu) - return LLDB_INVALID_INDEX32; - - // Setup control value - // Make the byte_mask into a valid Byte Address Select mask - control_value = byte_mask << 5; - - // Turn on appropriate watchpoint flags read or write - control_value |= (watch_flags << 3); - - // Enable this watchpoint and make it stop in privileged or user mode; - control_value |= 7; - - // Make sure bits 1:0 are clear in our address - addr &= ~((lldb::addr_t)3); - - // Iterate over stored watchpoints and find a free wp_index - wp_index = LLDB_INVALID_INDEX32; - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if ((m_hwp_regs[i].control & 1) == 0) { - wp_index = i; // Mark last free slot - } else if (m_hwp_regs[i].address == addr) { - return LLDB_INVALID_INDEX32; // We do not support duplicate watchpoints. - } - } - - if (wp_index == LLDB_INVALID_INDEX32) - return LLDB_INVALID_INDEX32; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].real_addr = real_addr; - m_hwp_regs[wp_index].address = addr; - m_hwp_regs[wp_index].control = control_value; - - // PTRACE call to set corresponding watchpoint register. - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, - wp_index); - - if (error.Fail()) { - m_hwp_regs[wp_index].address = 0; - m_hwp_regs[wp_index].control &= ~1; - - return LLDB_INVALID_INDEX32; - } - - return wp_index; -} - -bool NativeRegisterContextLinux_arm::ClearHardwareWatchpoint( - uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return false; - - if (wp_index >= m_max_hwp_supported) - return false; - - // Create a backup we can revert to in case of failure. - lldb::addr_t tempAddr = m_hwp_regs[wp_index].address; - uint32_t tempControl = m_hwp_regs[wp_index].control; - - // Update watchpoint in local cache - m_hwp_regs[wp_index].control &= ~1; - m_hwp_regs[wp_index].address = 0; - - // Ptrace call to update hardware debug registers - error = WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, - wp_index); - - if (error.Fail()) { - m_hwp_regs[wp_index].control = tempControl; - m_hwp_regs[wp_index].address = tempAddr; - - return false; - } - - return true; -} - -Status NativeRegisterContextLinux_arm::ClearAllHardwareWatchpoints() { - // Read hardware breakpoint and watchpoint information. - Status error = ReadHardwareDebugInfo(); - - if (error.Fail()) - return error; - - lldb::addr_t tempAddr = 0; - uint32_t tempControl = 0; - - for (uint32_t i = 0; i < m_max_hwp_supported; i++) { - if (m_hwp_regs[i].control & 0x01) { - // Create a backup we can revert to in case of failure. - tempAddr = m_hwp_regs[i].address; - tempControl = m_hwp_regs[i].control; - - // Clear watchpoints in local cache - m_hwp_regs[i].control &= ~1; - m_hwp_regs[i].address = 0; - - // Ptrace call to update hardware debug registers - error = - WriteHardwareDebugRegs(NativeRegisterContextDBReg::eDREGTypeWATCH, i); - - if (error.Fail()) { - m_hwp_regs[i].control = tempControl; - m_hwp_regs[i].address = tempAddr; - - return error; - } - } - } - - return Status(); -} - -uint32_t NativeRegisterContextLinux_arm::GetWatchpointSize(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) { - case 0x01: - return 1; - case 0x03: - return 2; - case 0x07: - return 3; - case 0x0f: - return 4; - default: - return 0; - } -} -bool NativeRegisterContextLinux_arm::WatchpointIsEnabled(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if ((m_hwp_regs[wp_index].control & 0x1) == 0x1) - return true; - else - return false; -} - -Status -NativeRegisterContextLinux_arm::GetWatchpointHitIndex(uint32_t &wp_index, - lldb::addr_t trap_addr) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}, trap_addr: {1:x}", wp_index, trap_addr); - - uint32_t watch_size; - lldb::addr_t watch_addr; - - for (wp_index = 0; wp_index < m_max_hwp_supported; ++wp_index) { - watch_size = GetWatchpointSize(wp_index); - watch_addr = m_hwp_regs[wp_index].address; - - if (WatchpointIsEnabled(wp_index) && trap_addr >= watch_addr && - trap_addr < watch_addr + watch_size) { - m_hwp_regs[wp_index].hit_addr = trap_addr; - return Status(); - } - } - - wp_index = LLDB_INVALID_INDEX32; - return Status(); -} - -lldb::addr_t -NativeRegisterContextLinux_arm::GetWatchpointAddress(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].real_addr; - else - return LLDB_INVALID_ADDRESS; -} - -lldb::addr_t -NativeRegisterContextLinux_arm::GetWatchpointHitAddress(uint32_t wp_index) { - Log *log = GetLog(POSIXLog::Watchpoints); - LLDB_LOG(log, "wp_index: {0}", wp_index); - - if (wp_index >= m_max_hwp_supported) - return LLDB_INVALID_ADDRESS; - - if (WatchpointIsEnabled(wp_index)) - return m_hwp_regs[wp_index].hit_addr; - else - return LLDB_INVALID_ADDRESS; -} - -Status NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { - Status error; - - if (!m_refresh_hwdebug_info) { - return Status(); - } +llvm::Error NativeRegisterContextLinux_arm::ReadHardwareDebugInfo() { + if (!m_refresh_hwdebug_info) + return llvm::Error::success(); #ifdef __arm__ unsigned int cap_val; - - error = NativeProcessLinux::PtraceWrapper(PTRACE_GETHBPREGS, m_thread.GetID(), - nullptr, &cap_val, - sizeof(unsigned int)); + Status error = NativeProcessLinux::PtraceWrapper( + PTRACE_GETHBPREGS, m_thread.GetID(), nullptr, &cap_val, + sizeof(unsigned int)); if (error.Fail()) - return error; + return error.ToError(); m_max_hwp_supported = (cap_val >> 8) & 0xff; m_max_hbp_supported = cap_val & 0xff; m_refresh_hwdebug_info = false; - return error; + return error.ToError(); #else // __aarch64__ return arm64::ReadHardwareDebugInfo(m_thread.GetID(), m_max_hwp_supported, - m_max_hbp_supported); + m_max_hbp_supported) + .ToError(); #endif // ifdef __arm__ } -Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( - NativeRegisterContextDBReg::DREGType hwbType, int hwb_index) { - Status error; - +llvm::Error +NativeRegisterContextLinux_arm::WriteHardwareDebugRegs(DREGType hwbType) { #ifdef __arm__ - lldb::addr_t *addr_buf; - uint32_t *ctrl_buf; - - if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) { - addr_buf = &m_hwp_regs[hwb_index].address; - ctrl_buf = &m_hwp_regs[hwb_index].control; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 1), addr_buf, - sizeof(unsigned int)); - - if (error.Fail()) - return error; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t) - ((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); - } else { - addr_buf = &m_hbr_regs[hwb_index].address; - ctrl_buf = &m_hbr_regs[hwb_index].control; - - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 1), addr_buf, - sizeof(unsigned int)); + uint32_t max_index = m_max_hbp_supported; + if (hwbType == eDREGTypeWATCH) + max_index = m_max_hwp_supported; - if (error.Fail()) + for (uint32_t idx = 0; idx < max_index; ++idx) + if (auto error = WriteHardwareDebugReg(hwbType, idx)) return error; - error = NativeProcessLinux::PtraceWrapper( - PTRACE_SETHBPREGS, m_thread.GetID(), - (PTRACE_TYPE_ARG3)(intptr_t)((hwb_index << 1) + 2), ctrl_buf, - sizeof(unsigned int)); - } - - return error; + return llvm::Error::success(); #else // __aarch64__ uint32_t max_supported = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) @@ -806,12 +327,48 @@ Status NativeRegisterContextLinux_arm::WriteHardwareDebugRegs( : m_max_hbp_supported; auto ®s = (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) ? m_hwp_regs - : m_hbr_regs; + : m_hbp_regs; return arm64::WriteHardwareDebugRegs(hwbType, m_thread.GetID(), max_supported, - regs); + regs) + .ToError(); #endif // ifdef __arm__ } +#ifdef __arm__ +llvm::Error +NativeRegisterContextLinux_arm::WriteHardwareDebugReg(DREGType hwbType, + int hwb_index) { + Status error; + lldb::addr_t *addr_buf; + uint32_t *ctrl_buf; + int addr_idx = (hwb_index << 1) + 1; + int ctrl_idx = addr_idx + 1; + + if (hwbType == NativeRegisterContextDBReg::eDREGTypeWATCH) { + addr_idx *= -1; + addr_buf = &m_hwp_regs[hwb_index].address; + ctrl_idx *= -1; + ctrl_buf = &m_hwp_regs[hwb_index].control; + } else { + addr_buf = &m_hbp_regs[hwb_index].address; + ctrl_buf = &m_hbp_regs[hwb_index].control; + } + + error = NativeProcessLinux::PtraceWrapper( + PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)addr_idx, + addr_buf, sizeof(unsigned int)); + + if (error.Fail()) + return error.ToError(); + + error = NativeProcessLinux::PtraceWrapper( + PTRACE_SETHBPREGS, m_thread.GetID(), (PTRACE_TYPE_ARG3)(intptr_t)ctrl_idx, + ctrl_buf, sizeof(unsigned int)); + + return error.ToError(); +} +#endif // ifdef __arm__ + uint32_t NativeRegisterContextLinux_arm::CalculateFprOffset( const RegisterInfo *reg_info) const { return reg_info->byte_offset - GetGPRSize(); diff --git a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h index 3a31d68..cf36859 100644 --- a/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h +++ b/lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm.h @@ -12,7 +12,7 @@ #define lldb_NativeRegisterContextLinux_arm_h #include "Plugins/Process/Linux/NativeRegisterContextLinux.h" -#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" +#include "Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h" #include "Plugins/Process/Utility/RegisterInfoPOSIX_arm.h" #include "Plugins/Process/Utility/lldb-arm-register-enums.h" @@ -21,7 +21,8 @@ namespace process_linux { class NativeProcessLinux; -class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux { +class NativeRegisterContextLinux_arm : public NativeRegisterContextLinux, + public NativeRegisterContextDBReg_arm { public: NativeRegisterContextLinux_arm(const ArchSpec &target_arch, NativeThreadProtocol &native_thread); @@ -42,39 +43,6 @@ public: Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; - // Hardware breakpoints/watchpoint management functions - - uint32_t NumSupportedHardwareBreakpoints() override; - - uint32_t SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; - - bool ClearHardwareBreakpoint(uint32_t hw_idx) override; - - Status ClearAllHardwareBreakpoints() override; - - Status GetHardwareBreakHitIndex(uint32_t &bp_index, - lldb::addr_t trap_addr) override; - - uint32_t NumSupportedHardwareWatchpoints() override; - - uint32_t SetHardwareWatchpoint(lldb::addr_t addr, size_t size, - uint32_t watch_flags) override; - - bool ClearHardwareWatchpoint(uint32_t hw_index) override; - - Status ClearAllHardwareWatchpoints() override; - - Status GetWatchpointHitIndex(uint32_t &wp_index, - lldb::addr_t trap_addr) override; - - lldb::addr_t GetWatchpointHitAddress(uint32_t wp_index) override; - - lldb::addr_t GetWatchpointAddress(uint32_t wp_index) override; - - uint32_t GetWatchpointSize(uint32_t wp_index); - - bool WatchpointIsEnabled(uint32_t wp_index); - protected: Status DoReadRegisterValue(uint32_t offset, const char *reg_name, uint32_t size, RegisterValue &value) override; @@ -100,23 +68,18 @@ private: uint32_t m_gpr_arm[k_num_gpr_registers_arm]; RegisterInfoPOSIX_arm::FPU m_fpr; - std::array<NativeRegisterContextDBReg::DREG, 16> - m_hbr_regs; // Arm native linux hardware breakpoints - std::array<NativeRegisterContextDBReg::DREG, 16> - m_hwp_regs; // Arm native linux hardware watchpoints - - uint32_t m_max_hwp_supported; - uint32_t m_max_hbp_supported; bool m_refresh_hwdebug_info; bool IsGPR(unsigned reg) const; bool IsFPR(unsigned reg) const; - Status ReadHardwareDebugInfo(); + llvm::Error ReadHardwareDebugInfo() override; - Status WriteHardwareDebugRegs(NativeRegisterContextDBReg::DREGType hwbType, - int hwb_index); + llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override; +#ifdef __arm__ + llvm::Error WriteHardwareDebugReg(DREGType hwbType, int hwb_index); +#endif uint32_t CalculateFprOffset(const RegisterInfo *reg_info) const; diff --git a/lldb/source/Plugins/Process/Utility/CMakeLists.txt b/lldb/source/Plugins/Process/Utility/CMakeLists.txt index 5d99c22..b1e326e 100644 --- a/lldb/source/Plugins/Process/Utility/CMakeLists.txt +++ b/lldb/source/Plugins/Process/Utility/CMakeLists.txt @@ -13,6 +13,7 @@ add_lldb_library(lldbPluginProcessUtility MemoryTagManagerAArch64MTE.cpp NativeProcessSoftwareSingleStep.cpp NativeRegisterContextDBReg.cpp + NativeRegisterContextDBReg_arm.cpp NativeRegisterContextDBReg_arm64.cpp NativeRegisterContextDBReg_loongarch.cpp NativeRegisterContextDBReg_x86.cpp diff --git a/lldb/source/Plugins/Process/Utility/HistoryThread.cpp b/lldb/source/Plugins/Process/Utility/HistoryThread.cpp index bc06757..93efa2a 100644 --- a/lldb/source/Plugins/Process/Utility/HistoryThread.cpp +++ b/lldb/source/Plugins/Process/Utility/HistoryThread.cpp @@ -27,13 +27,12 @@ using namespace lldb_private; HistoryThread::HistoryThread(lldb_private::Process &process, lldb::tid_t tid, std::vector<lldb::addr_t> pcs, - bool pcs_are_call_addresses) + HistoryPCType pc_type) : Thread(process, tid, true), m_framelist_mutex(), m_framelist(), m_pcs(pcs), m_extended_unwind_token(LLDB_INVALID_ADDRESS), m_queue_name(), m_thread_name(), m_originating_unique_thread_id(tid), m_queue_id(LLDB_INVALID_QUEUE_ID) { - m_unwinder_up = - std::make_unique<HistoryUnwind>(*this, pcs, pcs_are_call_addresses); + m_unwinder_up = std::make_unique<HistoryUnwind>(*this, pcs, pc_type); Log *log = GetLog(LLDBLog::Object); LLDB_LOGF(log, "%p HistoryThread::HistoryThread", static_cast<void *>(this)); } diff --git a/lldb/source/Plugins/Process/Utility/HistoryThread.h b/lldb/source/Plugins/Process/Utility/HistoryThread.h index a66e0f2..cdc3b09 100644 --- a/lldb/source/Plugins/Process/Utility/HistoryThread.h +++ b/lldb/source/Plugins/Process/Utility/HistoryThread.h @@ -27,14 +27,14 @@ namespace lldb_private { /// process execution /// /// This subclass of Thread is used to provide a backtrace from earlier in -/// process execution. It is given a backtrace list of pc addresses and it -/// will create stack frames for them. +/// process execution. It is given a backtrace list of pcs (return or call +/// addresses) and it will create stack frames for them. class HistoryThread : public lldb_private::Thread { public: HistoryThread(lldb_private::Process &process, lldb::tid_t tid, std::vector<lldb::addr_t> pcs, - bool pcs_are_call_addresses = false); + HistoryPCType pc_type = HistoryPCType::Returns); ~HistoryThread() override; diff --git a/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp b/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp index 7749dc6..3b0618f 100644 --- a/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp +++ b/lldb/source/Plugins/Process/Utility/HistoryUnwind.cpp @@ -24,9 +24,8 @@ using namespace lldb_private; // Constructor HistoryUnwind::HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs, - bool pcs_are_call_addresses) - : Unwind(thread), m_pcs(pcs), - m_pcs_are_call_addresses(pcs_are_call_addresses) {} + HistoryPCType pc_type) + : Unwind(thread), m_pcs(pcs), m_pc_type(pc_type) {} // Destructor @@ -52,6 +51,17 @@ HistoryUnwind::DoCreateRegisterContextForFrame(StackFrame *frame) { return rctx; } +static bool BehavesLikeZerothFrame(HistoryPCType pc_type, uint32_t frame_idx) { + switch (pc_type) { + case HistoryPCType::Returns: + return (frame_idx == 0); + case HistoryPCType::ReturnsNoZerothFrame: + return false; + case HistoryPCType::Calls: + return true; + } +} + bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, lldb::addr_t &pc, bool &behaves_like_zeroth_frame) { @@ -61,10 +71,7 @@ bool HistoryUnwind::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa, if (frame_idx < m_pcs.size()) { cfa = frame_idx; pc = m_pcs[frame_idx]; - if (m_pcs_are_call_addresses) - behaves_like_zeroth_frame = true; - else - behaves_like_zeroth_frame = (frame_idx == 0); + behaves_like_zeroth_frame = BehavesLikeZerothFrame(m_pc_type, frame_idx); return true; } return false; diff --git a/lldb/source/Plugins/Process/Utility/HistoryUnwind.h b/lldb/source/Plugins/Process/Utility/HistoryUnwind.h index cb72b5d..3cf70a4 100644 --- a/lldb/source/Plugins/Process/Utility/HistoryUnwind.h +++ b/lldb/source/Plugins/Process/Utility/HistoryUnwind.h @@ -19,7 +19,7 @@ namespace lldb_private { class HistoryUnwind : public lldb_private::Unwind { public: HistoryUnwind(Thread &thread, std::vector<lldb::addr_t> pcs, - bool pcs_are_call_addresses = false); + HistoryPCType pc_type = HistoryPCType::Returns); ~HistoryUnwind() override; @@ -36,9 +36,7 @@ protected: private: std::vector<lldb::addr_t> m_pcs; - /// This boolean indicates that the PCs in the non-0 frames are call - /// addresses and not return addresses. - bool m_pcs_are_call_addresses; + HistoryPCType m_pc_type; }; } // namespace lldb_private diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp index 19601b7..f35027e 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.cpp @@ -44,15 +44,16 @@ uint32_t NativeRegisterContextDBReg::SetHardwareBreakpoint(lldb::addr_t addr, return LLDB_INVALID_INDEX32; } - uint32_t control_value = 0, bp_index = 0; - if (!ValidateBreakpoint(size, addr)) return LLDB_INVALID_INDEX32; - control_value = MakeBreakControlValue(size); + uint32_t control_value = MakeBreakControlValue(size); + auto details = AdjustBreakpoint({size, addr}); + size = details.size; + addr = details.addr; // Iterate over stored breakpoints and find a free bp_index - bp_index = LLDB_INVALID_INDEX32; + uint32_t bp_index = LLDB_INVALID_INDEX32; for (uint32_t i = 0; i < m_max_hbp_supported; i++) { if (!BreakpointIsEnabled(i)) bp_index = i; // Mark last free slot @@ -222,7 +223,7 @@ uint32_t NativeRegisterContextDBReg::SetHardwareWatchpoint( addr = adjusted->addr; // Check if we are setting watchpoint other than read/write/access Also - // update watchpoint flag to match AArch64/LoongArch write-read bit + // update watchpoint flag to match ARM/AArch64/LoongArch write-read bit // configuration. switch (watch_flags) { case lldb::eWatchpointKindWrite: diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h index 9b6ecd3..2dd11dc 100644 --- a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg.h @@ -12,6 +12,7 @@ #include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" #include <array> +#include <optional> // Common utilities for hardware breakpoints and hardware watchpoints on AArch64 // and LoongArch. @@ -76,19 +77,25 @@ protected: // On AArch64 and Loongarch the hardware breakpoint length size is 4, and the // target address must 4-byte alignment. - bool ValidateBreakpoint(size_t size, lldb::addr_t addr) { + virtual bool ValidateBreakpoint(size_t size, lldb::addr_t addr) { return (size == 4) && !(addr & 0x3); } + struct WatchpointDetails { size_t size; lldb::addr_t addr; }; virtual std::optional<WatchpointDetails> AdjustWatchpoint(const WatchpointDetails &details) = 0; + + using BreakpointDetails = WatchpointDetails; + virtual BreakpointDetails AdjustBreakpoint(const BreakpointDetails &details) { + return details; + } + virtual uint32_t MakeBreakControlValue(size_t size) = 0; virtual uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) = 0; virtual uint32_t GetWatchpointSize(uint32_t wp_index) = 0; - virtual llvm::Error ReadHardwareDebugInfo() = 0; virtual llvm::Error WriteHardwareDebugRegs(DREGType hwbType) = 0; virtual lldb::addr_t FixWatchpointHitAddress(lldb::addr_t hit_addr) { diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp new file mode 100644 index 0000000..803fed5 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.cpp @@ -0,0 +1,117 @@ +//===-- NativeRegisterContextDBReg_arm.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 "NativeRegisterContextDBReg_arm.h" + +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" + +using namespace lldb_private; + +uint32_t NativeRegisterContextDBReg_arm::GetWatchpointSize(uint32_t wp_index) { + Log *log = GetLog(LLDBLog::Watchpoints); + LLDB_LOG(log, "wp_index: {0}", wp_index); + + switch ((m_hwp_regs[wp_index].control >> 5) & 0x0f) { + case 0x01: + return 1; + case 0x03: + return 2; + case 0x07: + return 3; + case 0x0f: + return 4; + default: + return 0; + } +} + +std::optional<NativeRegisterContextDBReg::WatchpointDetails> +NativeRegisterContextDBReg_arm::AdjustWatchpoint( + const WatchpointDetails &details) { + auto [size, addr] = details; + + if (size == 0 || size > 4) + return {}; + + // Check 4-byte alignment for hardware watchpoint target address. Below is a + // hack to recalculate address and size in order to make sure we can watch + // non 4-byte aligned addresses as well. + if (addr & 0x03) { + uint8_t watch_mask = (addr & 0x03) + size; + if (watch_mask > 0x04) + return {}; + else if (watch_mask <= 0x02) + size = 2; + else + size = 4; + + addr = addr & (~0x03); + } + + return WatchpointDetails{size, addr}; +} + +NativeRegisterContextDBReg::BreakpointDetails +NativeRegisterContextDBReg_arm::AdjustBreakpoint( + const BreakpointDetails &details) { + BreakpointDetails bd = details; + // Use size to get a hint of arm vs thumb modes. + // LLDB usually aligns this client side, but other clients may not. + switch (bd.size) { + case 2: + bd.addr &= ~1; + break; + case 4: + bd.addr &= ~3; + break; + default: + // We assume that ValidateBreakpoint would have caught this earlier. + llvm_unreachable("Invalid breakpoint size!"); + } + + return bd; +} + +uint32_t NativeRegisterContextDBReg_arm::MakeBreakControlValue(size_t size) { + switch (size) { + case 2: + return (0x3 << 5) | 7; + case 4: + return (0xfu << 5) | 7; + default: + // ValidateBreakpoint would have rejected this earlier. + llvm_unreachable("Invalid breakpoint size."); + } +} + +uint32_t +NativeRegisterContextDBReg_arm::MakeWatchControlValue(size_t size, + uint32_t watch_flags) { + // We can only watch up to four bytes that follow a 4 byte aligned address + // per watchpoint register pair, so make sure we can properly encode this. + // We assume that the address was 4 byte aligned by AdjustWatchpoint. + uint32_t byte_mask = (1u << size) - 1u; + + // Check if we need multiple watchpoint register + if (byte_mask > 0xfu) + return LLDB_INVALID_INDEX32; + + // Setup control value + // Make the byte_mask into a valid Byte Address Select mask + uint32_t control_value = byte_mask << 5; + + // Turn on appropriate watchpoint flags read or write + control_value |= (watch_flags << 3); + + // Enable this watchpoint and make it stop in privileged or user mode; + control_value |= 7; + + return control_value; +} diff --git a/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h new file mode 100644 index 0000000..253ae96 --- /dev/null +++ b/lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm.h @@ -0,0 +1,42 @@ +//===-- NativeRegisterContextDBReg_arm.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_NativeRegisterContextDBReg_arm_h +#define lldb_NativeRegisterContextDBReg_arm_h + +#include "Plugins/Process/Utility/NativeRegisterContextDBReg.h" + +namespace lldb_private { + +class NativeRegisterContextDBReg_arm : public NativeRegisterContextDBReg { +public: + NativeRegisterContextDBReg_arm() + : NativeRegisterContextDBReg(/*enable_bit=*/0x1U) {} + +private: + uint32_t GetWatchpointSize(uint32_t wp_index) override; + + std::optional<WatchpointDetails> + AdjustWatchpoint(const WatchpointDetails &details) override; + + BreakpointDetails AdjustBreakpoint(const BreakpointDetails &details) override; + + uint32_t MakeBreakControlValue(size_t size) override; + + uint32_t MakeWatchControlValue(size_t size, uint32_t watch_flags) override; + + bool ValidateBreakpoint(size_t size, + [[maybe_unused]] lldb::addr_t addr) override { + // Break on 4 or 2 byte instructions. + return size == 4 || size == 2; + } +}; + +} // namespace lldb_private + +#endif // #ifndef lldb_NativeRegisterContextDBReg_arm_h diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index 85e141d..91f3a6c 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -5773,7 +5773,7 @@ public: CommandObjectProcessGDBRemotePacketMonitor(CommandInterpreter &interpreter) : CommandObjectRaw(interpreter, "process plugin packet monitor", "Send a qRcmd packet through the GDB remote protocol " - "and print the response." + "and print the response. " "The argument passed to this command will be hex " "encoded into a valid 'qRcmd' packet, sent and the " "response will be printed.") {} diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp index c359663..12cb257 100644 --- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp +++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.cpp @@ -10,14 +10,15 @@ #include "Resource.h" #include "Tool.h" #include "lldb/Core/PluginManager.h" -#include "lldb/Protocol/MCP/MCPError.h" -#include "lldb/Protocol/MCP/Tool.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Protocol/MCP/Server.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Error.h" #include "llvm/Support/Threading.h" #include <thread> -#include <variant> using namespace lldb_private; using namespace lldb_private::mcp; @@ -26,24 +27,10 @@ using namespace llvm; LLDB_PLUGIN_DEFINE(ProtocolServerMCP) -static constexpr size_t kChunkSize = 1024; static constexpr llvm::StringLiteral kName = "lldb-mcp"; static constexpr llvm::StringLiteral kVersion = "0.1.0"; -ProtocolServerMCP::ProtocolServerMCP() - : ProtocolServer(), - lldb_protocol::mcp::Server(std::string(kName), std::string(kVersion)) { - AddNotificationHandler("notifications/initialized", - [](const lldb_protocol::mcp::Notification &) { - LLDB_LOG(GetLog(LLDBLog::Host), - "MCP initialization complete"); - }); - - AddTool( - std::make_unique<CommandTool>("lldb_command", "Run an lldb command.")); - - AddResourceProvider(std::make_unique<DebuggerResourceProvider>()); -} +ProtocolServerMCP::ProtocolServerMCP() : ProtocolServer() {} ProtocolServerMCP::~ProtocolServerMCP() { llvm::consumeError(Stop()); } @@ -53,6 +40,8 @@ void ProtocolServerMCP::Initialize() { } void ProtocolServerMCP::Terminate() { + if (llvm::Error error = ProtocolServer::Terminate()) + LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), "{0}"); PluginManager::UnregisterPlugin(CreateInstance); } @@ -64,57 +53,37 @@ llvm::StringRef ProtocolServerMCP::GetPluginDescriptionStatic() { return "MCP Server."; } +void ProtocolServerMCP::Extend(lldb_protocol::mcp::Server &server) const { + server.AddNotificationHandler("notifications/initialized", + [](const lldb_protocol::mcp::Notification &) { + LLDB_LOG(GetLog(LLDBLog::Host), + "MCP initialization complete"); + }); + server.AddTool( + std::make_unique<CommandTool>("lldb_command", "Run an lldb command.")); + server.AddResourceProvider(std::make_unique<DebuggerResourceProvider>()); +} + void ProtocolServerMCP::AcceptCallback(std::unique_ptr<Socket> socket) { - LLDB_LOG(GetLog(LLDBLog::Host), "New MCP client ({0}) connected", - m_clients.size() + 1); + Log *log = GetLog(LLDBLog::Host); + std::string client_name = llvm::formatv("client_{0}", m_instances.size() + 1); + LLDB_LOG(log, "New MCP client connected: {0}", client_name); lldb::IOObjectSP io_sp = std::move(socket); - auto client_up = std::make_unique<Client>(); - client_up->io_sp = io_sp; - Client *client = client_up.get(); - - Status status; - auto read_handle_up = m_loop.RegisterReadObject( - io_sp, - [this, client](MainLoopBase &loop) { - if (llvm::Error error = ReadCallback(*client)) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Host), std::move(error), "{0}"); - client->read_handle_up.reset(); - } - }, - status); - if (status.Fail()) + auto transport_up = std::make_unique<lldb_protocol::mcp::MCPTransport>( + io_sp, io_sp, std::move(client_name), [&](llvm::StringRef message) { + LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message); + }); + auto instance_up = std::make_unique<lldb_protocol::mcp::Server>( + std::string(kName), std::string(kVersion), std::move(transport_up), + m_loop); + Extend(*instance_up); + llvm::Error error = instance_up->Run(); + if (error) { + LLDB_LOG_ERROR(log, std::move(error), "Failed to run MCP server: {0}"); return; - - client_up->read_handle_up = std::move(read_handle_up); - m_clients.emplace_back(std::move(client_up)); -} - -llvm::Error ProtocolServerMCP::ReadCallback(Client &client) { - char chunk[kChunkSize]; - size_t bytes_read = sizeof(chunk); - if (Status status = client.io_sp->Read(chunk, bytes_read); status.Fail()) - return status.takeError(); - client.buffer.append(chunk, bytes_read); - - for (std::string::size_type pos; - (pos = client.buffer.find('\n')) != std::string::npos;) { - llvm::Expected<std::optional<lldb_protocol::mcp::Message>> message = - HandleData(StringRef(client.buffer.data(), pos)); - client.buffer = client.buffer.erase(0, pos + 1); - if (!message) - return message.takeError(); - - if (*message) { - std::string Output; - llvm::raw_string_ostream OS(Output); - OS << llvm::formatv("{0}", toJSON(**message)) << '\n'; - size_t num_bytes = Output.size(); - return client.io_sp->Write(Output.data(), num_bytes).takeError(); - } } - - return llvm::Error::success(); + m_instances.push_back(std::move(instance_up)); } llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) { @@ -138,6 +107,39 @@ llvm::Error ProtocolServerMCP::Start(ProtocolServer::Connection connection) { if (llvm::Error error = handles.takeError()) return error; + auto listening_uris = m_listener->GetListeningConnectionURI(); + if (listening_uris.empty()) + return createStringError("failed to get listening connections"); + std::string address = + llvm::join(m_listener->GetListeningConnectionURI(), ", "); + + FileSpec user_lldb_dir = HostInfo::GetUserLLDBDir(); + + Status error(llvm::sys::fs::create_directory(user_lldb_dir.GetPath())); + if (error.Fail()) + return error.takeError(); + + m_mcp_registry_entry_path = user_lldb_dir.CopyByAppendingPathComponent( + formatv("lldb-mcp-{0}.json", getpid()).str()); + + ServerInfo info; + info.connection_uri = listening_uris[0]; + info.pid = getpid(); + + std::string buf = formatv("{0}", toJSON(info)).str(); + size_t num_bytes = buf.size(); + + const File::OpenOptions flags = File::eOpenOptionWriteOnly | + File::eOpenOptionCanCreate | + File::eOpenOptionTruncate; + llvm::Expected<lldb::FileUP> file = + FileSystem::Instance().Open(m_mcp_registry_entry_path, flags, + lldb::eFilePermissionsFileDefault, false); + if (!file) + return file.takeError(); + if (llvm::Error error = (*file)->Write(buf.data(), num_bytes).takeError()) + return error; + m_running = true; m_listen_handlers = std::move(*handles); m_loop_thread = std::thread([=] { @@ -156,29 +158,17 @@ llvm::Error ProtocolServerMCP::Stop() { m_running = false; } + if (!m_mcp_registry_entry_path.GetPath().empty()) + FileSystem::Instance().RemoveFile(m_mcp_registry_entry_path); + m_mcp_registry_entry_path.Clear(); + // Stop the main loop. m_loop.AddPendingCallback( - [](MainLoopBase &loop) { loop.RequestTermination(); }); + [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); }); // Wait for the main loop to exit. if (m_loop_thread.joinable()) m_loop_thread.join(); - { - std::lock_guard<std::mutex> guard(m_mutex); - m_listener.reset(); - m_listen_handlers.clear(); - m_clients.clear(); - } - return llvm::Error::success(); } - -lldb_protocol::mcp::Capabilities ProtocolServerMCP::GetCapabilities() { - lldb_protocol::mcp::Capabilities capabilities; - capabilities.tools.listChanged = true; - // FIXME: Support sending notifications when a debugger/target are - // added/removed. - capabilities.resources.listChanged = false; - return capabilities; -} diff --git a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h index 7fe909a..004fa3c 100644 --- a/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h +++ b/lldb/source/Plugins/Protocol/MCP/ProtocolServerMCP.h @@ -18,8 +18,7 @@ namespace lldb_private::mcp { -class ProtocolServerMCP : public ProtocolServer, - public lldb_protocol::mcp::Server { +class ProtocolServerMCP : public ProtocolServer { public: ProtocolServerMCP(); virtual ~ProtocolServerMCP() override; @@ -39,26 +38,25 @@ public: Socket *GetSocket() const override { return m_listener.get(); } +protected: + // This adds tools and resource providers that + // are specific to this server. Overridable by the unit tests. + virtual void Extend(lldb_protocol::mcp::Server &server) const; + private: void AcceptCallback(std::unique_ptr<Socket> socket); - lldb_protocol::mcp::Capabilities GetCapabilities() override; - bool m_running = false; - MainLoop m_loop; + FileSpec m_mcp_registry_entry_path; + lldb_private::MainLoop m_loop; std::thread m_loop_thread; + std::mutex m_mutex; std::unique_ptr<Socket> m_listener; - std::vector<MainLoopBase::ReadHandleUP> m_listen_handlers; - struct Client { - lldb::IOObjectSP io_sp; - MainLoopBase::ReadHandleUP read_handle_up; - std::string buffer; - }; - llvm::Error ReadCallback(Client &client); - std::vector<std::unique_ptr<Client>> m_clients; + std::vector<MainLoopBase::ReadHandleUP> m_listen_handlers; + std::vector<std::unique_ptr<lldb_protocol::mcp::Server>> m_instances; }; } // namespace lldb_private::mcp diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.cpp b/lldb/source/Plugins/Protocol/MCP/Resource.cpp index e94d2cd..5814245 100644 --- a/lldb/source/Plugins/Protocol/MCP/Resource.cpp +++ b/lldb/source/Plugins/Protocol/MCP/Resource.cpp @@ -8,7 +8,6 @@ #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Protocol/MCP/MCPError.h" -#include "lldb/Target/Platform.h" using namespace lldb_private; using namespace lldb_private::mcp; @@ -124,7 +123,7 @@ DebuggerResourceProvider::GetResources() const { return resources; } -llvm::Expected<lldb_protocol::mcp::ResourceResult> +llvm::Expected<lldb_protocol::mcp::ReadResourceResult> DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const { auto [protocol, path] = uri.split("://"); @@ -161,7 +160,7 @@ DebuggerResourceProvider::ReadResource(llvm::StringRef uri) const { return ReadDebuggerResource(uri, debugger_idx); } -llvm::Expected<lldb_protocol::mcp::ResourceResult> +llvm::Expected<lldb_protocol::mcp::ReadResourceResult> DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t debugger_id) { lldb::DebuggerSP debugger_sp = Debugger::FindDebuggerWithID(debugger_id); @@ -173,17 +172,17 @@ DebuggerResourceProvider::ReadDebuggerResource(llvm::StringRef uri, debugger_resource.name = debugger_sp->GetInstanceName(); debugger_resource.num_targets = debugger_sp->GetTargetList().GetNumTargets(); - lldb_protocol::mcp::ResourceContents contents; + lldb_protocol::mcp::TextResourceContents contents; contents.uri = uri; contents.mimeType = kMimeTypeJSON; contents.text = llvm::formatv("{0}", toJSON(debugger_resource)); - lldb_protocol::mcp::ResourceResult result; + lldb_protocol::mcp::ReadResourceResult result; result.contents.push_back(contents); return result; } -llvm::Expected<lldb_protocol::mcp::ResourceResult> +llvm::Expected<lldb_protocol::mcp::ReadResourceResult> DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id, size_t target_idx) { @@ -209,12 +208,12 @@ DebuggerResourceProvider::ReadTargetResource(llvm::StringRef uri, if (lldb::PlatformSP platform_sp = target_sp->GetPlatform()) target_resource.platform = platform_sp->GetName(); - lldb_protocol::mcp::ResourceContents contents; + lldb_protocol::mcp::TextResourceContents contents; contents.uri = uri; contents.mimeType = kMimeTypeJSON; contents.text = llvm::formatv("{0}", toJSON(target_resource)); - lldb_protocol::mcp::ResourceResult result; + lldb_protocol::mcp::ReadResourceResult result; result.contents.push_back(contents); return result; } diff --git a/lldb/source/Plugins/Protocol/MCP/Resource.h b/lldb/source/Plugins/Protocol/MCP/Resource.h index e2382a7..0c65766 100644 --- a/lldb/source/Plugins/Protocol/MCP/Resource.h +++ b/lldb/source/Plugins/Protocol/MCP/Resource.h @@ -11,7 +11,11 @@ #include "lldb/Protocol/MCP/Protocol.h" #include "lldb/Protocol/MCP/Resource.h" -#include "lldb/lldb-private.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" +#include <cstddef> #include <vector> namespace lldb_private::mcp { @@ -21,9 +25,8 @@ public: using ResourceProvider::ResourceProvider; virtual ~DebuggerResourceProvider() = default; - virtual std::vector<lldb_protocol::mcp::Resource> - GetResources() const override; - virtual llvm::Expected<lldb_protocol::mcp::ResourceResult> + std::vector<lldb_protocol::mcp::Resource> GetResources() const override; + llvm::Expected<lldb_protocol::mcp::ReadResourceResult> ReadResource(llvm::StringRef uri) const override; private: @@ -31,9 +34,9 @@ private: static lldb_protocol::mcp::Resource GetTargetResource(size_t target_idx, Target &target); - static llvm::Expected<lldb_protocol::mcp::ResourceResult> + static llvm::Expected<lldb_protocol::mcp::ReadResourceResult> ReadDebuggerResource(llvm::StringRef uri, lldb::user_id_t debugger_id); - static llvm::Expected<lldb_protocol::mcp::ResourceResult> + static llvm::Expected<lldb_protocol::mcp::ReadResourceResult> ReadTargetResource(llvm::StringRef uri, lldb::user_id_t debugger_id, size_t target_idx); }; diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.cpp b/lldb/source/Plugins/Protocol/MCP/Tool.cpp index 14347070..2f451bf 100644 --- a/lldb/source/Plugins/Protocol/MCP/Tool.cpp +++ b/lldb/source/Plugins/Protocol/MCP/Tool.cpp @@ -7,9 +7,9 @@ //===----------------------------------------------------------------------===// #include "Tool.h" -#include "lldb/Core/Module.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Protocol/MCP/Protocol.h" using namespace lldb_private; using namespace lldb_protocol; @@ -29,10 +29,10 @@ bool fromJSON(const llvm::json::Value &V, CommandToolArguments &A, O.mapOptional("arguments", A.arguments); } -/// Helper function to create a TextResult from a string output. -static lldb_protocol::mcp::TextResult createTextResult(std::string output, - bool is_error = false) { - lldb_protocol::mcp::TextResult text_result; +/// Helper function to create a CallToolResult from a string output. +static lldb_protocol::mcp::CallToolResult +createTextResult(std::string output, bool is_error = false) { + lldb_protocol::mcp::CallToolResult text_result; text_result.content.emplace_back( lldb_protocol::mcp::TextContent{{std::move(output)}}); text_result.isError = is_error; @@ -41,7 +41,7 @@ static lldb_protocol::mcp::TextResult createTextResult(std::string output, } // namespace -llvm::Expected<lldb_protocol::mcp::TextResult> +llvm::Expected<lldb_protocol::mcp::CallToolResult> CommandTool::Call(const lldb_protocol::mcp::ToolArguments &args) { if (!std::holds_alternative<json::Value>(args)) return createStringError("CommandTool requires arguments"); diff --git a/lldb/source/Plugins/Protocol/MCP/Tool.h b/lldb/source/Plugins/Protocol/MCP/Tool.h index b7b1756..1886525 100644 --- a/lldb/source/Plugins/Protocol/MCP/Tool.h +++ b/lldb/source/Plugins/Protocol/MCP/Tool.h @@ -9,11 +9,11 @@ #ifndef LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H #define LLDB_PLUGINS_PROTOCOL_MCP_TOOL_H -#include "lldb/Core/Debugger.h" #include "lldb/Protocol/MCP/Protocol.h" #include "lldb/Protocol/MCP/Tool.h" +#include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" -#include <string> +#include <optional> namespace lldb_private::mcp { @@ -22,10 +22,10 @@ public: using lldb_protocol::mcp::Tool::Tool; ~CommandTool() = default; - virtual llvm::Expected<lldb_protocol::mcp::TextResult> + llvm::Expected<lldb_protocol::mcp::CallToolResult> Call(const lldb_protocol::mcp::ToolArguments &args) override; - virtual std::optional<llvm::json::Value> GetSchema() const override; + std::optional<llvm::json::Value> GetSchema() const override; }; } // namespace lldb_private::mcp diff --git a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp index f19dc8b..eb9e013 100644 --- a/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp +++ b/lldb/source/Plugins/RegisterTypeBuilder/RegisterTypeBuilderClang.cpp @@ -47,7 +47,7 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType( // See if we have made this type before and can reuse it. CompilerType fields_type = type_system->GetTypeForIdentifier<clang::CXXRecordDecl>( - register_type_name); + type_system->getASTContext(), register_type_name); if (!fields_type) { // In most ABI, a change of field type means a change in storage unit. @@ -83,7 +83,7 @@ CompilerType RegisterTypeBuilderClang::GetRegisterType( // may have built this one already. CompilerType field_enum_type = type_system->GetTypeForIdentifier<clang::EnumDecl>( - enum_type_name); + type_system->getASTContext(), enum_type_name); if (field_enum_type) field_type = field_enum_type; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index 27ac5432..a2a287a 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -405,15 +405,33 @@ Expected<llvm::StringRef> PythonString::AsUTF8() const { if (!IsValid()) return nullDeref(); - Py_ssize_t size; - const char *data; + // PyUnicode_AsUTF8AndSize caches the UTF-8 representation of the string in + // the Unicode object, which makes it more efficient and ties the lifetime of + // the data to the Python string. However, it was only added to the Stable API + // in Python 3.10. Older versions that want to use the Stable API must use + // PyUnicode_AsUTF8String in combination with ConstString. +#if defined(Py_LIMITED_API) && (Py_LIMITED_API < 0x030a0000) + PyObject *py_bytes = PyUnicode_AsUTF8String(m_py_obj); + if (!py_bytes) + return exception(); + auto release_py_str = + llvm::make_scope_exit([py_bytes] { Py_DECREF(py_bytes); }); + Py_ssize_t size = PyBytes_Size(py_bytes); + const char *str = PyBytes_AsString(py_bytes); + + if (!str) + return exception(); - data = PyUnicode_AsUTF8AndSize(m_py_obj, &size); + return ConstString(str, size).GetStringRef(); +#else + Py_ssize_t size; + const char *str = PyUnicode_AsUTF8AndSize(m_py_obj, &size); - if (!data) + if (!str) return exception(); - return llvm::StringRef(data, size); + return llvm::StringRef(str, size); +#endif } size_t PythonString::GetSize() const { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 24d604f..9330a63 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -92,10 +92,23 @@ namespace { struct InitializePythonRAII { public: InitializePythonRAII() { + // The table of built-in modules can only be extended before Python is + // initialized. + if (!Py_IsInitialized()) { +#ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE + // Python's readline is incompatible with libedit being linked into lldb. + // Provide a patched version local to the embedded interpreter. + PyImport_AppendInittab("readline", initlldb_readline); +#endif + + // Register _lldb as a built-in module. + PyImport_AppendInittab("_lldb", LLDBSwigPyInit); + } + +#if LLDB_EMBED_PYTHON_HOME PyConfig config; PyConfig_InitPythonConfig(&config); -#if LLDB_EMBED_PYTHON_HOME static std::string g_python_home = []() -> std::string { if (llvm::sys::path::is_absolute(LLDB_PYTHON_HOME)) return LLDB_PYTHON_HOME; @@ -109,34 +122,13 @@ public: if (!g_python_home.empty()) { PyConfig_SetBytesString(&config, &config.home, g_python_home.c_str()); } -#endif - - // The table of built-in modules can only be extended before Python is - // initialized. - if (!Py_IsInitialized()) { -#ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE - // Python's readline is incompatible with libedit being linked into lldb. - // Provide a patched version local to the embedded interpreter. - bool ReadlinePatched = false; - for (auto *p = PyImport_Inittab; p->name != nullptr; p++) { - if (strcmp(p->name, "readline") == 0) { - p->initfunc = initlldb_readline; - break; - } - } - if (!ReadlinePatched) { - PyImport_AppendInittab("readline", initlldb_readline); - ReadlinePatched = true; - } -#endif - - // Register _lldb as a built-in module. - PyImport_AppendInittab("_lldb", LLDBSwigPyInit); - } config.install_signal_handlers = 0; Py_InitializeFromConfig(&config); PyConfig_Clear(&config); +#else + Py_InitializeEx(/*install_sigs=*/0); +#endif // The only case we should go further and acquire the GIL: it is unlocked. PyGILState_STATE gil_state = PyGILState_Ensure(); @@ -907,11 +899,11 @@ bool ScriptInterpreterPythonImpl::Interrupt() { Log *log = GetLog(LLDBLog::Script); if (IsExecutingPython()) { - PyThreadState *state = PyThreadState_GET(); + PyThreadState *state = PyThreadState_Get(); if (!state) state = GetThreadState(); if (state) { - long tid = state->thread_id; + long tid = PyThread_get_thread_ident(); PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); LLDB_LOGF(log, diff --git a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp index 867f6a6..70093c9 100644 --- a/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp +++ b/lldb/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp @@ -1601,6 +1601,7 @@ void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) { const char *func_name = "_libtrace_init"; const lldb::addr_t offset = 0; + const bool offset_is_insn_count = false; const LazyBool skip_prologue = eLazyBoolCalculate; // This is an internal breakpoint - the user shouldn't see it. const bool internal = true; @@ -1608,7 +1609,8 @@ void StructuredDataDarwinLog::AddInitCompletionHook(Process &process) { auto breakpoint_sp = target.CreateBreakpoint( &module_spec_list, source_spec_list, func_name, eFunctionNameTypeFull, - eLanguageTypeC, offset, skip_prologue, internal, hardware); + eLanguageTypeC, offset, offset_is_insn_count, skip_prologue, internal, + hardware); if (!breakpoint_sp) { // Huh? Bail here. LLDB_LOGF(log, diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp index a9345c7..1bfa17f 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDIE.cpp @@ -44,7 +44,7 @@ class ElaboratingDIEIterator // Container sizes are optimized for the case of following DW_AT_specification // and DW_AT_abstract_origin just once. llvm::SmallVector<DWARFDIE, 2> m_worklist; - llvm::SmallSet<DWARFDebugInfoEntry *, 3> m_seen; + llvm::SmallPtrSet<DWARFDebugInfoEntry *, 3> m_seen; void Next() { assert(!m_worklist.empty() && "Incrementing end iterator?"); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp index f968eee..849f573 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -284,9 +284,10 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges( /// Adds all attributes of the DIE at the top of the \c worklist to the /// \c attributes list. Specifcations and abstract origins are added /// to the \c worklist if the referenced DIE has not been seen before. -static bool GetAttributes(llvm::SmallVectorImpl<DWARFDIE> &worklist, - llvm::SmallSet<DWARFDebugInfoEntry const *, 3> &seen, - DWARFAttributes &attributes) { +static bool +GetAttributes(llvm::SmallVectorImpl<DWARFDIE> &worklist, + llvm::SmallPtrSet<DWARFDebugInfoEntry const *, 3> &seen, + DWARFAttributes &attributes) { assert(!worklist.empty() && "Need at least one DIE to visit."); assert(seen.size() >= 1 && "Need to have seen at least the currently visited entry."); @@ -366,7 +367,7 @@ DWARFAttributes DWARFDebugInfoEntry::GetAttributes(const DWARFUnit *cu, // Keep track if DIEs already seen to prevent infinite recursion. // Value of '3' was picked for the same reason that // DWARFDie::findRecursively does. - llvm::SmallSet<DWARFDebugInfoEntry const *, 3> seen; + llvm::SmallPtrSet<DWARFDebugInfoEntry const *, 3> seen; seen.insert(this); do { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 84b3da3..b15e0c1 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -8,6 +8,7 @@ #include "SymbolFileDWARF.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Support/Casting.h" @@ -998,12 +999,12 @@ XcodeSDK SymbolFileDWARF::ParseXcodeSDK(CompileUnit &comp_unit) { const char *sdk = cu_die.GetAttributeValueAsString(DW_AT_APPLE_sdk, nullptr); if (!sdk) return {}; - std::string sysroot = + llvm::StringRef sysroot = cu_die.GetAttributeValueAsString(DW_AT_LLVM_sysroot, ""); // RegisterXcodeSDK calls into xcrun which is not aware of CLT, which is // expensive. - if (sysroot.find("/Library/Developer/CommandLineTools/SDKs") != 0) { + if (!sysroot.starts_with("/Library/Developer/CommandLineTools/SDKs")) { // Register the sysroot path remapping with the module belonging to // the CU as well as the one belonging to the symbol file. The two // would be different if this is an OSO object and module is the @@ -1017,7 +1018,7 @@ XcodeSDK SymbolFileDWARF::ParseXcodeSDK(CompileUnit &comp_unit) { local_module_sp->RegisterXcodeSDK(sdk, sysroot); } - return {sdk, FileSpec{std::move(sysroot)}}; + return {sdk, FileSpec(sysroot)}; } size_t SymbolFileDWARF::ParseFunctions(CompileUnit &comp_unit) { @@ -2483,6 +2484,30 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } +DWARFDIE +SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label) { + DWARFDIE definition; + Module::LookupInfo info(ConstString(label.lookup_name), + lldb::eFunctionNameTypeFull, + lldb::eLanguageTypeUnknown); + + m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { + if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) + return IterationAction::Continue; + + // We don't check whether the specification DIE for this function + // corresponds to the declaration DIE because the declaration might be in + // a type-unit but the definition in the compile-unit (and it's + // specifcation would point to the declaration in the compile-unit). We + // rely on the mangled name within the module to be enough to find us the + // unique definition. + definition = entry; + return IterationAction::Stop; + }); + + return definition; +} + llvm::Expected<SymbolContext> SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); @@ -2495,37 +2520,19 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { // Label was created using a declaration DIE. Need to fetch the definition // to resolve the function call. if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) { - Module::LookupInfo info(ConstString(label.lookup_name), - lldb::eFunctionNameTypeFull, - lldb::eLanguageTypeUnknown); - - m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { - if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) - return IterationAction::Continue; - - // We don't check whether the specification DIE for this function - // corresponds to the declaration DIE because the declaration might be in - // a type-unit but the definition in the compile-unit (and it's - // specifcation would point to the declaration in the compile-unit). We - // rely on the mangled name within the module to be enough to find us the - // unique definition. - die = entry; - return IterationAction::Stop; - }); + auto definition = FindFunctionDefinition(label); + if (!definition) + return llvm::createStringError("failed to find definition DIE"); - if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) - return llvm::createStringError( - llvm::formatv("failed to find definition DIE for {0}", label)); + die = std::move(definition); } SymbolContextList sc_list; if (!ResolveFunction(die, /*include_inlines=*/false, sc_list)) - return llvm::createStringError( - llvm::formatv("failed to resolve function for {0}", label)); + return llvm::createStringError("failed to resolve function"); if (sc_list.IsEmpty()) - return llvm::createStringError( - llvm::formatv("failed to find function for {0}", label)); + return llvm::createStringError("failed to find function"); assert(sc_list.GetSize() == 1); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 5042d91..d7db8a3 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -373,6 +373,13 @@ public: /// Returns the DWARFIndex for this symbol, if it exists. DWARFIndex *getIndex() { return m_index.get(); } +private: + /// Find the definition DIE for the specified \c label in this + /// SymbolFile. + /// + /// \returns A valid definition DIE on success. + DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label); + protected: SymbolFileDWARF(const SymbolFileDWARF &) = delete; const SymbolFileDWARF &operator=(const SymbolFileDWARF &) = delete; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp index f01fba3..709281c 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -449,7 +449,7 @@ bool PdbAstBuilder::CompleteTagDecl(clang::TagDecl &tag) { ->GetIndex(); lldbassert(IsTagRecord(type_id, index.tpi())); - clang::QualType tag_qt = m_clang.getASTContext().getTypeDeclType(&tag); + clang::QualType tag_qt = m_clang.getASTContext().getCanonicalTagType(&tag); TypeSystemClang::SetHasExternalStorage(tag_qt.getAsOpaquePtr(), false); TypeIndex tag_ti = type_id.index; @@ -562,7 +562,8 @@ clang::QualType PdbAstBuilder::CreatePointerType(const PointerRecord &pointer) { m_clang.getASTContext(), spelling)); } return m_clang.getASTContext().getMemberPointerType( - pointee_type, /*Qualifier=*/nullptr, class_type->getAsCXXRecordDecl()); + pointee_type, /*Qualifier=*/std::nullopt, + class_type->getAsCXXRecordDecl()); } clang::QualType pointer_type; @@ -862,9 +863,9 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, SymbolFileNativePDB *pdb = static_cast<SymbolFileNativePDB *>( m_clang.GetSymbolFile()->GetBackingSymbolFile()); PdbIndex &index = pdb->GetIndex(); - clang::QualType parent_qt = llvm::cast<clang::TypeDecl>(parent) - ->getTypeForDecl() - ->getCanonicalTypeInternal(); + clang::CanQualType parent_qt = + m_clang.getASTContext().getCanonicalTypeDeclType( + llvm::cast<clang::TypeDecl>(parent)); lldb::opaque_compiler_type_t parent_opaque_ty = ToCompilerType(parent_qt).GetOpaqueQualType(); // FIXME: Remove this workaround. diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp index dcea33d..112eb06 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp @@ -39,6 +39,7 @@ #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" #include "llvm/DebugInfo/PDB/Native/NativeSession.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" #include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/DebugInfo/PDB/PDB.h" @@ -643,8 +644,14 @@ SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id, std::string uname = GetUnqualifiedTypeName(record); - // FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE. + llvm::Expected<Declaration> maybeDecl = ResolveUdtDeclaration(type_id); Declaration decl; + if (maybeDecl) + decl = std::move(*maybeDecl); + else + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), maybeDecl.takeError(), + "Failed to resolve declaration for '{1}': {0}", uname); + return MakeType(toOpaqueUid(type_id), ConstString(uname), size, nullptr, LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct, Type::ResolveState::Forward); @@ -667,7 +674,14 @@ lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id, CompilerType ct) { std::string uname = GetUnqualifiedTypeName(er); + llvm::Expected<Declaration> maybeDecl = ResolveUdtDeclaration(type_id); Declaration decl; + if (maybeDecl) + decl = std::move(*maybeDecl); + else + LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), maybeDecl.takeError(), + "Failed to resolve declaration for '{1}': {0}", uname); + TypeSP underlying_type = GetOrCreateType(er.UnderlyingType); return MakeType( @@ -1641,6 +1655,94 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) { clang->GetNativePDBParser()->Dump(s, filter); } +void SymbolFileNativePDB::CacheFunctionNames() { + if (!m_func_full_names.IsEmpty()) + return; + + // (segment, code offset) -> gid + std::map<std::pair<uint16_t, uint32_t>, uint32_t> addr_ids; + + // First, find all function references in the globals table. + for (const uint32_t gid : m_index->globals().getGlobalsTable()) { + CVSymbol ref_sym = m_index->symrecords().readRecord(gid); + auto kind = ref_sym.kind(); + if (kind != S_PROCREF && kind != S_LPROCREF) + continue; + + ProcRefSym ref = + llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(ref_sym)); + if (ref.Name.empty()) + continue; + + // Find the function this is referencing. + CompilandIndexItem &cci = + m_index->compilands().GetOrCreateCompiland(ref.modi()); + auto iter = cci.m_debug_stream.getSymbolArray().at(ref.SymOffset); + if (iter == cci.m_debug_stream.getSymbolArray().end()) + continue; + kind = iter->kind(); + if (kind != S_GPROC32 && kind != S_LPROC32) + continue; + + ProcSym proc = + llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(*iter)); + if ((proc.Flags & ProcSymFlags::IsUnreachable) != ProcSymFlags::None) + continue; + if (proc.Name.empty() || proc.FunctionType.isSimple()) + continue; + + // The function/procedure symbol only contains the demangled name. + // The mangled names are in the publics table. Save the address of this + // function to lookup the mangled name later. + addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid); + + llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(proc.Name); + if (basename.empty()) + basename = proc.Name; + + m_func_base_names.Append(ConstString(basename), gid); + m_func_full_names.Append(ConstString(proc.Name), gid); + + // To see if this is a member function, check the type. + auto type = m_index->tpi().getType(proc.FunctionType); + if (type.kind() == LF_MFUNCTION) { + MemberFunctionRecord mfr; + llvm::cantFail( + TypeDeserializer::deserializeAs<MemberFunctionRecord>(type, mfr)); + if (!mfr.getThisType().isNoneType()) + m_func_method_names.Append(ConstString(basename), gid); + } + } + + // The publics stream contains all mangled function names and their address. + for (auto pid : m_index->publics().getPublicsTable()) { + PdbGlobalSymId global{pid, true}; + CVSymbol sym = m_index->ReadSymbolRecord(global); + auto kind = sym.kind(); + if (kind != S_PUB32) + continue; + PublicSym32 pub = + llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym)); + // We only care about mangled names - if the name isn't mangled, it's + // already in the full name map. + if (!Mangled::IsMangledName(pub.Name)) + continue; + + // Check if this symbol is for one of our functions. + auto it = addr_ids.find({pub.Segment, pub.Offset}); + if (it != addr_ids.end()) + m_func_full_names.Append(ConstString(pub.Name), it->second); + } + + // Sort them before value searching is working properly. + m_func_full_names.Sort(); + m_func_full_names.SizeToFit(); + m_func_method_names.Sort(); + m_func_method_names.SizeToFit(); + m_func_base_names.Sort(); + m_func_base_names.SizeToFit(); +} + void SymbolFileNativePDB::FindGlobalVariables( ConstString name, const CompilerDeclContext &parent_decl_ctx, uint32_t max_matches, VariableList &variables) { @@ -1677,34 +1779,60 @@ void SymbolFileNativePDB::FindFunctions( if (name_type_mask & eFunctionNameTypeFull) name = lookup_info.GetName(); - // For now we only support lookup by method name or full name. if (!(name_type_mask & eFunctionNameTypeFull || + name_type_mask & eFunctionNameTypeBase || name_type_mask & eFunctionNameTypeMethod)) return; + CacheFunctionNames(); - using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>; + std::set<uint32_t> resolved_ids; // avoid duplicate lookups + auto resolve_from = [&](UniqueCStringMap<uint32_t> &Names) { + std::vector<uint32_t> ids; + if (!Names.GetValues(name, ids)) + return; - std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName( - name.GetStringRef(), m_index->symrecords()); - for (const SymbolAndOffset &match : matches) { - if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF) - continue; - ProcRefSym proc(match.second.kind()); - cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc)); + for (uint32_t id : ids) { + if (!resolved_ids.insert(id).second) + continue; - if (!IsValidRecord(proc)) - continue; + PdbGlobalSymId global{id, false}; + if (parent_decl_ctx.IsValid() && + GetDeclContextContainingUID(toOpaqueUid(global)) != parent_decl_ctx) + continue; - CompilandIndexItem &cci = - m_index->compilands().GetOrCreateCompiland(proc.modi()); - SymbolContext sc; + CVSymbol sym = m_index->ReadSymbolRecord(global); + auto kind = sym.kind(); + lldbassert(kind == S_PROCREF || kind == S_LPROCREF); - sc.comp_unit = GetOrCreateCompileUnit(cci).get(); - PdbCompilandSymId func_id(proc.modi(), proc.SymOffset); - sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get(); + ProcRefSym proc = + cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(sym)); - sc_list.Append(sc); - } + if (!IsValidRecord(proc)) + continue; + + CompilandIndexItem &cci = + m_index->compilands().GetOrCreateCompiland(proc.modi()); + SymbolContext sc; + + sc.comp_unit = GetOrCreateCompileUnit(cci).get(); + if (!sc.comp_unit) + continue; + + PdbCompilandSymId func_id(proc.modi(), proc.SymOffset); + sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get(); + if (!sc.function) + continue; + + sc_list.Append(sc); + } + }; + + if (name_type_mask & eFunctionNameTypeFull) + resolve_from(m_func_full_names); + if (name_type_mask & eFunctionNameTypeBase) + resolve_from(m_func_base_names); + if (name_type_mask & eFunctionNameTypeMethod) + resolve_from(m_func_method_names); } void SymbolFileNativePDB::FindFunctions(const RegularExpression ®ex, @@ -2441,3 +2569,70 @@ SymbolFileNativePDB::GetContextForType(TypeIndex ti) { } return ctx; } + +void SymbolFileNativePDB::CacheUdtDeclarations() { + for (CVType cvt : m_index->ipi().typeArray()) { + switch (cvt.kind()) { + case LF_UDT_SRC_LINE: { + UdtSourceLineRecord udt_src; + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, udt_src)); + m_udt_declarations.try_emplace( + udt_src.UDT, UdtDeclaration{/*FileNameIndex=*/udt_src.SourceFile, + /*IsIpiIndex=*/true, + /*Line=*/udt_src.LineNumber}); + } break; + case LF_UDT_MOD_SRC_LINE: { + UdtModSourceLineRecord udt_mod_src; + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, udt_mod_src)); + // Some types might be contributed by multiple modules. We assume that + // they all point to the same file and line because we can only provide + // one location. + m_udt_declarations.try_emplace( + udt_mod_src.UDT, + UdtDeclaration{/*FileNameIndex=*/udt_mod_src.SourceFile, + /*IsIpiIndex=*/false, + /*Line=*/udt_mod_src.LineNumber}); + } break; + default: + break; + } + } +} + +llvm::Expected<Declaration> +SymbolFileNativePDB::ResolveUdtDeclaration(PdbTypeSymId type_id) { + std::call_once(m_cached_udt_declarations, [this] { CacheUdtDeclarations(); }); + + auto it = m_udt_declarations.find(type_id.index); + if (it == m_udt_declarations.end()) + return llvm::createStringError("No UDT declaration found"); + + llvm::StringRef file_name; + if (it->second.IsIpiIndex) { + CVType cvt = m_index->ipi().getType(it->second.FileNameIndex); + if (cvt.kind() != LF_STRING_ID) + return llvm::createStringError("File name was not a LF_STRING_ID"); + + StringIdRecord sid; + llvm::cantFail(TypeDeserializer::deserializeAs(cvt, sid)); + file_name = sid.String; + } else { + // The file name index is an index into the string table + auto string_table = m_index->pdb().getStringTable(); + if (!string_table) + return string_table.takeError(); + + llvm::Expected<llvm::StringRef> string = + string_table->getStringTable().getString( + it->second.FileNameIndex.getIndex()); + if (!string) + return string.takeError(); + file_name = *string; + } + + // rustc sets the filename to "<unknown>" for some files + if (file_name == "\\<unknown>") + return Declaration(); + + return Declaration(FileSpec(file_name), it->second.Line); +} diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h index eda375d..cfa0041 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h +++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h @@ -260,6 +260,11 @@ private: std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti); + void CacheFunctionNames(); + + void CacheUdtDeclarations(); + llvm::Expected<Declaration> ResolveUdtDeclaration(PdbTypeSymId type_id); + llvm::BumpPtrAllocator m_allocator; lldb::addr_t m_obj_load_address = 0; @@ -281,7 +286,26 @@ private: llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex> m_parent_types; + struct UdtDeclaration { + /// This could either be an index into the `/names` section (string table, + /// LF_UDT_MOD_SRC_LINE) or, this could be an index into the IPI stream to a + /// LF_STRING_ID record (LF_UDT_SRC_LINE). + llvm::codeview::TypeIndex FileNameIndex; + bool IsIpiIndex; + + uint32_t Line; + }; + llvm::DenseMap<llvm::codeview::TypeIndex, UdtDeclaration> m_udt_declarations; + std::once_flag m_cached_udt_declarations; + lldb_private::UniqueCStringMap<uint32_t> m_type_base_names; + + /// mangled name/full function name -> Global ID(s) + lldb_private::UniqueCStringMap<uint32_t> m_func_full_names; + /// basename -> Global ID(s) + lldb_private::UniqueCStringMap<uint32_t> m_func_base_names; + /// method basename -> Global ID(s) + lldb_private::UniqueCStringMap<uint32_t> m_func_method_names; }; } // namespace npdb diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp index 8b8eac6e..3a95588 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -407,8 +407,8 @@ lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) { // symbols in PDB for types with const or volatile modifiers, but we need // to create only one declaration for them all. Type::ResolveState type_resolve_state; - CompilerType clang_type = - m_ast.GetTypeForIdentifier<clang::CXXRecordDecl>(name, decl_context); + CompilerType clang_type = m_ast.GetTypeForIdentifier<clang::CXXRecordDecl>( + m_ast.getASTContext(), name, decl_context); if (!clang_type.IsValid()) { auto access = GetAccessibilityForUdt(*udt); @@ -479,8 +479,8 @@ lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) { uint64_t bytes = enum_type->getLength(); // Check if such an enum already exists in the current context - CompilerType ast_enum = - m_ast.GetTypeForIdentifier<clang::EnumDecl>(name, decl_context); + CompilerType ast_enum = m_ast.GetTypeForIdentifier<clang::EnumDecl>( + m_ast.getASTContext(), name, decl_context); if (!ast_enum.IsValid()) { auto underlying_type_up = enum_type->getUnderlyingType(); if (!underlying_type_up) @@ -557,7 +557,8 @@ lldb::TypeSP PDBASTParser::CreateLLDBTypeFromPDBType(const PDBSymbol &type) { // Check if such a typedef already exists in the current context CompilerType ast_typedef = - m_ast.GetTypeForIdentifier<clang::TypedefNameDecl>(name, decl_ctx); + m_ast.GetTypeForIdentifier<clang::TypedefNameDecl>( + m_ast.getASTContext(), name, decl_ctx); if (!ast_typedef.IsValid()) { CompilerType target_ast_type = target_type->GetFullCompilerType(); diff --git a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp index b23f642..04a25e4 100644 --- a/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp +++ b/lldb/source/Plugins/SystemRuntime/MacOSX/SystemRuntimeMacOSX.cpp @@ -544,9 +544,9 @@ ThreadSP SystemRuntimeMacOSX::GetExtendedBacktraceThread(ThreadSP real_thread, if (!thread_extended_info->ForEach(extract_frame_pc)) return {}; - originating_thread_sp = - std::make_shared<HistoryThread>(*m_process, real_thread->GetIndexID(), - app_specific_backtrace_pcs, true); + originating_thread_sp = std::make_shared<HistoryThread>( + *m_process, real_thread->GetIndexID(), app_specific_backtrace_pcs, + HistoryPCType::Calls); originating_thread_sp->SetQueueName(type.AsCString()); } return originating_thread_sp; diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 6addf4f..39aacdb 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -27,6 +27,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Mangle.h" +#include "clang/AST/QualTypeNames.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" #include "clang/AST/VTableBuilder.h" @@ -161,8 +162,7 @@ void addOverridesForMethod(clang::CXXMethodDecl *decl) { auto find_overridden_methods = [&decls, decl](const clang::CXXBaseSpecifier *specifier, clang::CXXBasePath &path) { - if (auto *base_record = llvm::dyn_cast<clang::CXXRecordDecl>( - specifier->getType()->castAs<clang::RecordType>()->getDecl())) { + if (auto *base_record = specifier->getType()->getAsCXXRecordDecl()) { clang::DeclarationName name = decl->getDeclName(); @@ -1179,7 +1179,7 @@ CompilerType TypeSystemClang::GetTypeForDecl(clang::NamedDecl *decl) { } CompilerType TypeSystemClang::GetTypeForDecl(TagDecl *decl) { - return GetType(getASTContext().getTagDeclType(decl)); + return GetType(getASTContext().getCanonicalTagType(decl)); } CompilerType TypeSystemClang::GetTypeForDecl(ObjCInterfaceDecl *decl) { @@ -1306,7 +1306,7 @@ CompilerType TypeSystemClang::CreateRecordType( if (decl_ctx) decl_ctx->addDecl(decl); - return GetType(ast.getTagDeclType(decl)); + return GetType(ast.getCanonicalTagType(decl)); } namespace { @@ -1674,7 +1674,6 @@ TypeSystemClang::CreateClassTemplateSpecializationDecl( class_template_specialization_decl->setInstantiationOf(class_template_decl); class_template_specialization_decl->setTemplateArgs( TemplateArgumentList::CreateCopy(ast, args)); - ast.getTypeDeclType(class_template_specialization_decl, nullptr); class_template_specialization_decl->setDeclName( class_template_decl->getDeclName()); @@ -1696,7 +1695,7 @@ CompilerType TypeSystemClang::CreateClassTemplateSpecializationType( ClassTemplateSpecializationDecl *class_template_specialization_decl) { if (class_template_specialization_decl) { ASTContext &ast = getASTContext(); - return GetType(ast.getTagDeclType(class_template_specialization_decl)); + return GetType(ast.getCanonicalTagType(class_template_specialization_decl)); } return CompilerType(); } @@ -1792,9 +1791,7 @@ bool TypeSystemClang::RecordHasFields(const RecordDecl *record_decl) { for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { - const CXXRecordDecl *base_class_decl = cast<CXXRecordDecl>( - base_class->getType()->getAs<RecordType>()->getDecl()); - if (RecordHasFields(base_class_decl)) + if (RecordHasFields(base_class->getType()->getAsCXXRecordDecl())) return true; } } @@ -2290,9 +2287,9 @@ CompilerType TypeSystemClang::CreateStructForIdentifier( &type_fields, bool packed) { CompilerType type; - if (!type_name.empty() && - (type = GetTypeForIdentifier<clang::CXXRecordDecl>(type_name)) - .IsValid()) { + if (!type_name.empty() && (type = GetTypeForIdentifier<clang::CXXRecordDecl>( + getASTContext(), type_name)) + .IsValid()) { lldbassert(0 && "Trying to create a type for an existing name"); return type; } @@ -2316,7 +2313,9 @@ CompilerType TypeSystemClang::GetOrCreateStructForIdentifier( &type_fields, bool packed) { CompilerType type; - if ((type = GetTypeForIdentifier<clang::CXXRecordDecl>(type_name)).IsValid()) + if ((type = GetTypeForIdentifier<clang::CXXRecordDecl>(getASTContext(), + type_name)) + .IsValid()) return type; return CreateStructForIdentifier(type_name, type_fields, packed); @@ -2355,7 +2354,7 @@ CompilerType TypeSystemClang::CreateEnumerationType( enum_decl->setAccess(AS_public); // TODO respect what's in the debug info - return GetType(ast.getTagDeclType(enum_decl)); + return GetType(ast.getCanonicalTagType(enum_decl)); } CompilerType TypeSystemClang::GetIntTypeFromBitSize(size_t bit_size, @@ -2471,7 +2470,7 @@ bool TypeSystemClang::GetCompleteDecl(clang::ASTContext *ast, ast_source->CompleteType(tag_decl); - return !tag_decl->getTypeForDecl()->isIncompleteType(); + return !ast->getCanonicalTagType(tag_decl)->isIncompleteType(); } else if (clang::ObjCInterfaceDecl *objc_interface_decl = llvm::dyn_cast<clang::ObjCInterfaceDecl>(decl)) { if (objc_interface_decl->getDefinition()) @@ -2575,7 +2574,6 @@ RemoveWrappingTypes(QualType type, ArrayRef<clang::Type::TypeClass> mask = {}) { break; case clang::Type::Auto: case clang::Type::Decltype: - case clang::Type::Elaborated: case clang::Type::Paren: case clang::Type::SubstTemplateTypeParm: case clang::Type::TemplateSpecialization: @@ -2607,10 +2605,11 @@ TypeSystemClang::GetDeclContextForType(clang::QualType type) { return GetDeclContextForType( llvm::cast<clang::ObjCObjectPointerType>(qual_type.getTypePtr()) ->getPointeeType()); - case clang::Type::Record: - return llvm::cast<clang::RecordType>(qual_type)->getDecl(); case clang::Type::Enum: - return llvm::cast<clang::EnumType>(qual_type)->getDecl(); + case clang::Type::Record: + return llvm::cast<clang::TagType>(qual_type) + ->getOriginalDecl() + ->getDefinitionOrSelf(); default: break; } @@ -2790,7 +2789,7 @@ static bool GetCompleteQualType(clang::ASTContext *ast, if (ast->getTargetInfo().getCXXABI().isMicrosoft()) { auto *MPT = qual_type.getTypePtr()->castAs<clang::MemberPointerType>(); if (auto *RD = MPT->getMostRecentCXXRecordDecl()) - GetCompleteRecordType(ast, QualType(RD->getTypeForDecl(), 0), + GetCompleteRecordType(ast, ast->getCanonicalTagType(RD), allow_completion); return !qual_type.getTypePtr()->isIncompleteType(); @@ -2859,7 +2858,8 @@ bool TypeSystemClang::IsAnonymousType(lldb::opaque_compiler_type_t type) { if (const clang::RecordType *record_type = llvm::dyn_cast_or_null<clang::RecordType>( qual_type.getTypePtrOrNull())) { - if (const clang::RecordDecl *record_decl = record_type->getDecl()) { + if (const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()) { return record_decl->isAnonymousStructOrUnion(); } } @@ -3099,8 +3099,8 @@ TypeSystemClang::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); if (record_type) { - const clang::RecordDecl *record_decl = record_type->getDecl(); - if (record_decl) { + if (const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinition()) { // We are looking for a structure that contains only floating point // types clang::RecordDecl::field_iterator field_pos, @@ -3280,7 +3280,10 @@ bool TypeSystemClang::IsEnumerationType(lldb::opaque_compiler_type_t type, GetCanonicalQualType(type)->getCanonicalTypeInternal()); if (enum_type) { - IsIntegerType(enum_type->getDecl()->getIntegerType().getAsOpaquePtr(), + IsIntegerType(enum_type->getOriginalDecl() + ->getDefinitionOrSelf() + ->getIntegerType() + .getAsOpaquePtr(), is_signed); return true; } @@ -3505,8 +3508,7 @@ bool TypeSystemClang::IsDefined(lldb::opaque_compiler_type_t type) { 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 (clang::TagDecl *tag_decl = tag_type->getOriginalDecl()->getDefinition()) return tag_decl->isCompleteDefinition(); return false; } else { @@ -3565,21 +3567,14 @@ bool TypeSystemClang::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { - const clang::RecordType *record_type = - llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); - if (record_decl) { - const clang::CXXRecordDecl *cxx_record_decl = - llvm::dyn_cast<clang::CXXRecordDecl>(record_decl); - if (cxx_record_decl) { - // We can't just call is isPolymorphic() here because that just - // means the current class has virtual functions, it doesn't check - // if any inherited classes have virtual functions. The doc string - // in SBType::IsPolymorphicClass() says it is looking for both - // if the class has virtual methods or if any bases do, so this - // should be more correct. - return cxx_record_decl->isDynamicClass(); - } + if (const auto *cxx_record_decl = qual_type->getAsCXXRecordDecl()) { + // We can't just call is isPolymorphic() here because that just + // means the current class has virtual functions, it doesn't check + // if any inherited classes have virtual functions. The doc string + // in SBType::IsPolymorphicClass() says it is looking for both + // if the class has virtual methods or if any bases do, so this + // should be more correct. + return cxx_record_decl->isDynamicClass(); } } break; @@ -3766,7 +3761,7 @@ bool TypeSystemClang::IsBeingDefined(lldb::opaque_compiler_type_t type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::TagType *tag_type = llvm::dyn_cast<clang::TagType>(qual_type); if (tag_type) - return tag_type->isBeingDefined(); + return tag_type->getOriginalDecl()->isEntityBeingDefined(); return false; } @@ -3974,7 +3969,8 @@ TypeSystemClang::GetTypeInfo(lldb::opaque_compiler_type_t type, if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( weak_from_this(), llvm::cast<clang::EnumType>(qual_type) - ->getDecl() + ->getOriginalDecl() + ->getDefinitionOrSelf() ->getIntegerType() .getAsOpaquePtr()); return eTypeIsEnumeration | eTypeHasValue; @@ -4154,7 +4150,6 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) { case clang::Type::Auto: case clang::Type::CountAttributed: case clang::Type::Decltype: - case clang::Type::Elaborated: case clang::Type::Paren: case clang::Type::TypeOf: case clang::Type::TypeOfExpr: @@ -4214,7 +4209,7 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) { case clang::Type::Record: { const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); + const clang::RecordDecl *record_decl = record_type->getOriginalDecl(); if (record_decl->isUnion()) return lldb::eTypeClassUnion; else if (record_decl->isStruct()) @@ -4280,6 +4275,8 @@ TypeSystemClang::GetTypeClass(lldb::opaque_compiler_type_t type) { break; case clang::Type::HLSLInlineSpirv: break; + case clang::Type::SubstBuiltinTemplatePack: + break; } // We don't know hot to display this type... return lldb::eTypeClassOther; @@ -4412,17 +4409,10 @@ TypeSystemClang::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) { clang::QualType qual_type = RemoveWrappingTypes(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::Record: - if (GetCompleteQualType(&getASTContext(), qual_type)) { - const clang::RecordType *record_type = - llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); - assert(record_decl); - const clang::CXXRecordDecl *cxx_record_decl = - llvm::dyn_cast<clang::CXXRecordDecl>(record_decl); - if (cxx_record_decl) + if (GetCompleteQualType(&getASTContext(), qual_type)) + if (const auto *cxx_record_decl = qual_type->getAsCXXRecordDecl()) num_functions = std::distance(cxx_record_decl->method_begin(), cxx_record_decl->method_end()); - } break; case clang::Type::ObjCObjectPointer: { @@ -4477,13 +4467,7 @@ TypeSystemClang::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type, switch (qual_type->getTypeClass()) { case clang::Type::Record: if (GetCompleteQualType(&getASTContext(), qual_type)) { - const clang::RecordType *record_type = - llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); - assert(record_decl); - const clang::CXXRecordDecl *cxx_record_decl = - llvm::dyn_cast<clang::CXXRecordDecl>(record_decl); - if (cxx_record_decl) { + if (const auto *cxx_record_decl = qual_type->getAsCXXRecordDecl()) { auto method_iter = cxx_record_decl->method_begin(); auto method_end = cxx_record_decl->method_end(); if (idx < @@ -4703,9 +4687,9 @@ CompilerType TypeSystemClang::CreateTypedef( clang::TagDecl *tdecl = nullptr; if (!qual_type.isNull()) { if (const clang::RecordType *rt = qual_type->getAs<clang::RecordType>()) - tdecl = rt->getDecl(); + tdecl = rt->getOriginalDecl(); if (const clang::EnumType *et = qual_type->getAs<clang::EnumType>()) - tdecl = et->getDecl(); + tdecl = et->getOriginalDecl(); } // Check whether this declaration is an anonymous struct, union, or enum, @@ -4717,7 +4701,10 @@ CompilerType TypeSystemClang::CreateTypedef( decl->setAccess(clang::AS_public); // TODO respect proper access specifier // Get a uniqued clang::QualType for the typedef decl type - return GetType(clang_ast.getTypedefType(decl)); + NestedNameSpecifier Qualifier = + clang::TypeName::getFullyQualifiedDeclaredContext(clang_ast, decl); + return GetType( + clang_ast.getTypedefType(ElaboratedTypeKeyword::None, Qualifier, decl)); } return CompilerType(); } @@ -4869,7 +4856,6 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::Type::Auto: case clang::Type::CountAttributed: case clang::Type::Decltype: - case clang::Type::Elaborated: case clang::Type::Paren: case clang::Type::Typedef: case clang::Type::TypeOf: @@ -5155,6 +5141,8 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, break; case clang::Type::HLSLInlineSpirv: break; + case clang::Type::SubstBuiltinTemplatePack: + break; } count = 0; return lldb::eEncodingInvalid; @@ -5171,7 +5159,6 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) { case clang::Type::Auto: case clang::Type::CountAttributed: case clang::Type::Decltype: - case clang::Type::Elaborated: case clang::Type::Paren: case clang::Type::Typedef: case clang::Type::TypeOf: @@ -5324,6 +5311,8 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) { break; case clang::Type::HLSLInlineSpirv: break; + case clang::Type::SubstBuiltinTemplatePack: + break; } // We don't know hot to display this type... return lldb::eFormatBytes; @@ -5380,8 +5369,8 @@ TypeSystemClang::GetNumChildren(lldb::opaque_compiler_type_t type, if (GetCompleteQualType(&getASTContext(), qual_type)) { const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); - assert(record_decl); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(record_decl); @@ -5577,7 +5566,8 @@ void TypeSystemClang::ForEachEnumerator( const clang::EnumType *enum_type = llvm::dyn_cast<clang::EnumType>(GetCanonicalQualType(type)); if (enum_type) { - const clang::EnumDecl *enum_decl = enum_type->getDecl(); + const clang::EnumDecl *enum_decl = + enum_type->getOriginalDecl()->getDefinitionOrSelf(); if (enum_decl) { CompilerType integer_type = GetType(enum_decl->getIntegerType()); @@ -5608,7 +5598,8 @@ uint32_t TypeSystemClang::GetNumFields(lldb::opaque_compiler_type_t type) { const clang::RecordType *record_type = llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr()); if (record_type) { - clang::RecordDecl *record_decl = record_type->getDecl(); + clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinition(); if (record_decl) { count = std::distance(record_decl->field_begin(), record_decl->field_end()); @@ -5722,7 +5713,8 @@ CompilerType TypeSystemClang::GetFieldAtIndex(lldb::opaque_compiler_type_t type, if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), @@ -5908,7 +5900,7 @@ CompilerType TypeSystemClang::GetDirectBaseClassAtIndex( llvm::cast<clang::CXXRecordDecl>( base_class->getType() ->castAs<clang::RecordType>() - ->getDecl()); + ->getOriginalDecl()); if (base_class->isVirtual()) *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl) @@ -6003,7 +5995,7 @@ CompilerType TypeSystemClang::GetVirtualBaseClassAtIndex( llvm::cast<clang::CXXRecordDecl>( base_class->getType() ->castAs<clang::RecordType>() - ->getDecl()); + ->getOriginalDecl()); *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * @@ -6033,7 +6025,8 @@ TypeSystemClang::GetStaticFieldWithName(lldb::opaque_compiler_type_t type, const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); clang::DeclarationName decl_name(&getASTContext().Idents.get(name)); for (NamedDecl *decl : record_decl->lookup(decl_name)) { @@ -6263,8 +6256,8 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex( if (idx_is_valid && GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast<clang::RecordType>(parent_qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); - assert(record_decl); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); const clang::ASTRecordLayout &record_layout = getASTContext().getASTRecordLayout(record_decl); uint32_t child_idx = 0; @@ -6283,7 +6276,10 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex( // Skip empty base classes if (omit_empty_base_classes) { base_class_decl = llvm::cast<clang::CXXRecordDecl>( - base_class->getType()->getAs<clang::RecordType>()->getDecl()); + base_class->getType() + ->getAs<clang::RecordType>() + ->getOriginalDecl()) + ->getDefinitionOrSelf(); if (!TypeSystemClang::RecordHasFields(base_class_decl)) continue; } @@ -6291,7 +6287,10 @@ llvm::Expected<CompilerType> TypeSystemClang::GetChildCompilerTypeAtIndex( if (idx == child_idx) { if (base_class_decl == nullptr) base_class_decl = llvm::cast<clang::CXXRecordDecl>( - base_class->getType()->getAs<clang::RecordType>()->getDecl()); + base_class->getType() + ->getAs<clang::RecordType>() + ->getOriginalDecl()) + ->getDefinitionOrSelf(); if (base_class->isVirtual()) { bool handled = false; @@ -6752,7 +6751,8 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); assert(record_decl); uint32_t child_idx = 0; @@ -6817,10 +6817,10 @@ size_t TypeSystemClang::GetIndexOfChildMemberWithName( return 0; } else { child_indexes.push_back(child_idx); - parent_record_decl = llvm::cast<clang::RecordDecl>( - elem.Base->getType() - ->castAs<clang::RecordType>() - ->getDecl()); + parent_record_decl = elem.Base->getType() + ->castAs<clang::RecordType>() + ->getOriginalDecl() + ->getDefinitionOrSelf(); } } for (clang::DeclContext::lookup_iterator I = path->Decls, E; @@ -6954,7 +6954,8 @@ TypeSystemClang::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); assert(record_decl); uint32_t child_idx = 0; @@ -6973,7 +6974,8 @@ TypeSystemClang::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, llvm::cast<clang::CXXRecordDecl>( base_class->getType() ->castAs<clang::RecordType>() - ->getDecl()); + ->getOriginalDecl()) + ->getDefinitionOrSelf(); if (omit_empty_base_classes && !TypeSystemClang::RecordHasFields(base_class_decl)) continue; @@ -7092,14 +7094,17 @@ TypeSystemClang::GetDirectNestedTypeWithName(lldb::opaque_compiler_type_t type, return CompilerType(); const clang::RecordType *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); clang::DeclarationName decl_name(&getASTContext().Idents.get(name)); for (NamedDecl *decl : record_decl->lookup(decl_name)) { if (auto *tag_decl = dyn_cast<clang::TagDecl>(decl)) - return GetType(getASTContext().getTagDeclType(tag_decl)); + return GetType(getASTContext().getCanonicalTagType(tag_decl)); if (auto *typedef_decl = dyn_cast<clang::TypedefNameDecl>(decl)) - return GetType(getASTContext().getTypedefType(typedef_decl)); + return GetType(getASTContext().getTypedefType( + ElaboratedTypeKeyword::None, /*Qualifier=*/std::nullopt, + typedef_decl)); } break; } @@ -7116,7 +7121,7 @@ bool TypeSystemClang::IsTemplateType(lldb::opaque_compiler_type_t type) { const clang::Type *clang_type = ClangUtil::GetQualType(ct).getTypePtr(); if (auto *cxx_record_decl = dyn_cast<clang::TagType>(clang_type)) return isa<clang::ClassTemplateSpecializationDecl>( - cxx_record_decl->getDecl()); + cxx_record_decl->getOriginalDecl()); return false; } @@ -7319,7 +7324,7 @@ clang::EnumDecl *TypeSystemClang::GetAsEnumDecl(const CompilerType &type) { const clang::EnumType *enutype = llvm::dyn_cast<clang::EnumType>(ClangUtil::GetCanonicalQualType(type)); if (enutype) - return enutype->getDecl(); + return enutype->getOriginalDecl()->getDefinitionOrSelf(); return nullptr; } @@ -7327,7 +7332,7 @@ clang::RecordDecl *TypeSystemClang::GetAsRecordDecl(const CompilerType &type) { const clang::RecordType *record_type = llvm::dyn_cast<clang::RecordType>(ClangUtil::GetCanonicalQualType(type)); if (record_type) - return record_type->getDecl(); + return record_type->getOriginalDecl()->getDefinitionOrSelf(); return nullptr; } @@ -7409,7 +7414,7 @@ clang::FieldDecl *TypeSystemClang::AddFieldToRecordType( if (const clang::TagType *TagT = field->getType()->getAs<clang::TagType>()) { if (clang::RecordDecl *Rec = - llvm::dyn_cast<clang::RecordDecl>(TagT->getDecl())) + llvm::dyn_cast<clang::RecordDecl>(TagT->getOriginalDecl())) if (!Rec->getDeclName()) { Rec->setAnonymousStructOrUnion(true); field->setImplicit(); @@ -7494,7 +7499,8 @@ void TypeSystemClang::BuildIndirectFields(const CompilerType &type) { if (!field_record_type) continue; - clang::RecordDecl *field_record_decl = field_record_type->getDecl(); + clang::RecordDecl *field_record_decl = + field_record_type->getOriginalDecl()->getDefinition(); if (!field_record_decl) continue; @@ -7636,7 +7642,8 @@ void TypeSystemClang::SetIntegerInitializerForVariable( // If the variable is an enum type, take the underlying integer type as // the type of the integer literal. if (const EnumType *enum_type = qt->getAs<EnumType>()) { - const EnumDecl *enum_decl = enum_type->getDecl(); + const EnumDecl *enum_decl = + enum_type->getOriginalDecl()->getDefinitionOrSelf(); qt = enum_decl->getIntegerType(); } // Bools are handled separately because the clang AST printer handles bools @@ -8296,7 +8303,7 @@ bool TypeSystemClang::SetHasExternalStorage(lldb::opaque_compiler_type_t type, case clang::Type::Enum: { clang::EnumDecl *enum_decl = - llvm::cast<clang::EnumType>(qual_type)->getDecl(); + llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl(); if (enum_decl) { enum_decl->setHasExternalLexicalStorage(has_extern); enum_decl->setHasExternalVisibleStorage(has_extern); @@ -8334,7 +8341,7 @@ bool TypeSystemClang::StartTagDeclarationDefinition(const CompilerType &type) { if (!qual_type.isNull()) { const clang::TagType *tag_type = qual_type->getAs<clang::TagType>(); if (tag_type) { - clang::TagDecl *tag_decl = tag_type->getDecl(); + clang::TagDecl *tag_decl = tag_type->getOriginalDecl(); if (tag_decl) { tag_decl->startDefinition(); return true; @@ -8369,7 +8376,8 @@ bool TypeSystemClang::CompleteTagDeclarationDefinition( // the definition. const clang::TagType *tag_type = qual_type->getAs<clang::TagType>(); if (tag_type) { - clang::TagDecl *tag_decl = tag_type->getDecl(); + clang::TagDecl *tag_decl = + tag_type->getOriginalDecl()->getDefinitionOrSelf(); if (auto *cxx_record_decl = llvm::dyn_cast<CXXRecordDecl>(tag_decl)) { // If we have a move constructor declared but no copy constructor we @@ -8404,7 +8412,8 @@ bool TypeSystemClang::CompleteTagDeclarationDefinition( if (!enutype) return false; - clang::EnumDecl *enum_decl = enutype->getDecl(); + clang::EnumDecl *enum_decl = + enutype->getOriginalDecl()->getDefinitionOrSelf(); if (enum_decl->isCompleteDefinition()) return true; @@ -8462,17 +8471,19 @@ clang::EnumConstantDecl *TypeSystemClang::AddEnumerationValueToEnumerationType( clang::EnumConstantDecl *enumerator_decl = clang::EnumConstantDecl::CreateDeserialized(getASTContext(), GlobalDeclID()); - enumerator_decl->setDeclContext(enutype->getDecl()); + clang::EnumDecl *enum_decl = + enutype->getOriginalDecl()->getDefinitionOrSelf(); + enumerator_decl->setDeclContext(enum_decl); if (name && name[0]) enumerator_decl->setDeclName(&getASTContext().Idents.get(name)); enumerator_decl->setType(clang::QualType(enutype, 0)); enumerator_decl->setInitVal(getASTContext(), value); - SetMemberOwningModule(enumerator_decl, enutype->getDecl()); + SetMemberOwningModule(enumerator_decl, enum_decl); if (!enumerator_decl) return nullptr; - enutype->getDecl()->addDecl(enumerator_decl); + enum_decl->addDecl(enumerator_decl); VerifyDecl(enumerator_decl); return enumerator_decl; @@ -8496,7 +8507,8 @@ CompilerType TypeSystemClang::GetEnumerationIntegerType(CompilerType type) { if (!enum_type) return CompilerType(); - return GetType(enum_type->getDecl()->getIntegerType()); + return GetType( + enum_type->getOriginalDecl()->getDefinitionOrSelf()->getIntegerType()); } CompilerType @@ -8509,7 +8521,7 @@ TypeSystemClang::CreateMemberPointerType(const CompilerType &type, return CompilerType(); return ast->GetType(ast->getASTContext().getMemberPointerType( ClangUtil::GetQualType(pointee_type), - /*Qualifier=*/nullptr, + /*Qualifier=*/std::nullopt, ClangUtil::GetQualType(type)->getAsCXXRecordDecl())); } return CompilerType(); @@ -8587,8 +8599,8 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s, uint32_t bitfield_bit_size) { const clang::EnumType *enutype = llvm::cast<clang::EnumType>(qual_type.getTypePtr()); - const clang::EnumDecl *enum_decl = enutype->getDecl(); - assert(enum_decl); + const clang::EnumDecl *enum_decl = + enutype->getOriginalDecl()->getDefinitionOrSelf(); lldb::offset_t offset = byte_offset; bool qual_type_is_signed = qual_type->isSignedIntegerOrEnumerationType(); const uint64_t enum_svalue = @@ -8694,15 +8706,7 @@ bool TypeSystemClang::DumpTypeValue( } else { clang::QualType qual_type(GetQualType(type)); - const clang::Type::TypeClass type_class = qual_type->getTypeClass(); - - if (type_class == clang::Type::Elaborated) { - qual_type = llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType(); - return DumpTypeValue(qual_type.getAsOpaquePtr(), s, format, data, byte_offset, byte_size, - bitfield_bit_size, bitfield_bit_offset, exe_scope); - } - - switch (type_class) { + switch (qual_type->getTypeClass()) { case clang::Type::Typedef: { clang::QualType typedef_qual_type = llvm::cast<clang::TypedefType>(qual_type) @@ -8872,7 +8876,7 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type, GetCompleteType(type); auto *record_type = llvm::cast<clang::RecordType>(qual_type.getTypePtr()); - const clang::RecordDecl *record_decl = record_type->getDecl(); + const clang::RecordDecl *record_decl = record_type->getOriginalDecl(); if (level == eDescriptionLevelVerbose) record_decl->dump(llvm_ostrm); else { @@ -8884,7 +8888,7 @@ void TypeSystemClang::DumpTypeDescription(lldb::opaque_compiler_type_t type, default: { if (auto *tag_type = llvm::dyn_cast<clang::TagType>(qual_type.getTypePtr())) { - if (clang::TagDecl *tag_decl = tag_type->getDecl()) { + if (clang::TagDecl *tag_decl = tag_type->getOriginalDecl()) { if (level == eDescriptionLevelVerbose) tag_decl->dump(llvm_ostrm); else @@ -8924,7 +8928,7 @@ void TypeSystemClang::DumpTypeName(const CompilerType &type) { case clang::Type::Enum: { clang::EnumDecl *enum_decl = - llvm::cast<clang::EnumType>(qual_type)->getDecl(); + llvm::cast<clang::EnumType>(qual_type)->getOriginalDecl(); if (enum_decl) { printf("enum %s", enum_decl->getName().str().c_str()); } @@ -8960,13 +8964,6 @@ void TypeSystemClang::DumpTypeName(const CompilerType &type) { ->getDeducedType() .getAsOpaquePtr())); - case clang::Type::Elaborated: - printf("elaborated "); - return DumpTypeName(CompilerType( - type.GetTypeSystem(), llvm::cast<clang::ElaboratedType>(qual_type) - ->getNamedType() - .getAsOpaquePtr())); - case clang::Type::Paren: printf("paren "); return DumpTypeName(CompilerType( @@ -9796,8 +9793,8 @@ bool TypeSystemClang::IsForcefullyCompleted(lldb::opaque_compiler_type_t type) { const clang::RecordType *record_type = llvm::dyn_cast<clang::RecordType>(qual_type.getTypePtr()); if (record_type) { - const clang::RecordDecl *record_decl = record_type->getDecl(); - assert(record_decl); + const clang::RecordDecl *record_decl = + record_type->getOriginalDecl()->getDefinitionOrSelf(); if (std::optional<ClangASTMetadata> metadata = GetMetadata(record_decl)) return metadata->IsForcefullyCompleted(); } diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 70d613d..709f8959 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -260,7 +260,7 @@ public: template <typename RecordDeclType> CompilerType - GetTypeForIdentifier(llvm::StringRef type_name, + GetTypeForIdentifier(const clang::ASTContext &Ctx, llvm::StringRef type_name, clang::DeclContext *decl_context = nullptr) { CompilerType compiler_type; if (type_name.empty()) @@ -278,11 +278,10 @@ public: return compiler_type; clang::NamedDecl *named_decl = *result.begin(); - if (const RecordDeclType *record_decl = - llvm::dyn_cast<RecordDeclType>(named_decl)) + if (const auto *type_decl = llvm::dyn_cast<clang::TypeDecl>(named_decl); + llvm::isa_and_nonnull<RecordDeclType>(type_decl)) compiler_type = CompilerType( - weak_from_this(), - clang::QualType(record_decl->getTypeForDecl(), 0).getAsOpaquePtr()); + weak_from_this(), Ctx.getTypeDeclType(type_decl).getAsOpaquePtr()); return compiler_type; } diff --git a/lldb/source/Protocol/MCP/CMakeLists.txt b/lldb/source/Protocol/MCP/CMakeLists.txt index a73e7e6..a4f270a 100644 --- a/lldb/source/Protocol/MCP/CMakeLists.txt +++ b/lldb/source/Protocol/MCP/CMakeLists.txt @@ -7,6 +7,7 @@ add_lldb_library(lldbProtocolMCP NO_PLUGIN_DEPENDENCIES LINK_COMPONENTS Support LINK_LIBS + lldbHost lldbUtility ) diff --git a/lldb/source/Protocol/MCP/MCPError.cpp b/lldb/source/Protocol/MCP/MCPError.cpp index c610e88..e140d11 100644 --- a/lldb/source/Protocol/MCP/MCPError.cpp +++ b/lldb/source/Protocol/MCP/MCPError.cpp @@ -25,10 +25,10 @@ std::error_code MCPError::convertToErrorCode() const { return llvm::inconvertibleErrorCode(); } -lldb_protocol::mcp::Error MCPError::toProtcolError() const { +lldb_protocol::mcp::Error MCPError::toProtocolError() const { lldb_protocol::mcp::Error error; - error.error.code = m_error_code; - error.error.message = m_message; + error.code = m_error_code; + error.message = m_message; return error; } diff --git a/lldb/source/Protocol/MCP/Protocol.cpp b/lldb/source/Protocol/MCP/Protocol.cpp index d579b88..0988f45 100644 --- a/lldb/source/Protocol/MCP/Protocol.cpp +++ b/lldb/source/Protocol/MCP/Protocol.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "lldb/Protocol/MCP/Protocol.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/JSON.h" using namespace llvm; @@ -26,8 +27,45 @@ static bool mapRaw(const json::Value &Params, StringLiteral Prop, return true; } +static llvm::json::Value toJSON(const Id &Id) { + if (const int64_t *I = std::get_if<int64_t>(&Id)) + return json::Value(*I); + if (const std::string *S = std::get_if<std::string>(&Id)) + return json::Value(*S); + llvm_unreachable("unexpected type in protocol::Id"); +} + +static bool mapId(const llvm::json::Value &V, StringLiteral Prop, Id &Id, + llvm::json::Path P) { + const auto *O = V.getAsObject(); + if (!O) { + P.report("expected object"); + return false; + } + + const auto *E = O->get(Prop); + if (!E) { + P.field(Prop).report("not found"); + return false; + } + + if (auto S = E->getAsString()) { + Id = S->str(); + return true; + } + + if (auto I = E->getAsInteger()) { + Id = *I; + return true; + } + + P.report("expected string or number"); + return false; +} + llvm::json::Value toJSON(const Request &R) { - json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}, {"method", R.method}}; + json::Object Result{ + {"jsonrpc", "2.0"}, {"id", toJSON(R.id)}, {"method", R.method}}; if (R.params) Result.insert({"params", R.params}); return Result; @@ -35,47 +73,75 @@ llvm::json::Value toJSON(const Request &R) { bool fromJSON(const llvm::json::Value &V, Request &R, llvm::json::Path P) { llvm::json::ObjectMapper O(V, P); - if (!O || !O.map("id", R.id) || !O.map("method", R.method)) - return false; - return mapRaw(V, "params", R.params, P); + return O && mapId(V, "id", R.id, P) && O.map("method", R.method) && + mapRaw(V, "params", R.params, P); } -llvm::json::Value toJSON(const ErrorInfo &EI) { - llvm::json::Object Result{{"code", EI.code}, {"message", EI.message}}; - if (!EI.data.empty()) - Result.insert({"data", EI.data}); - return Result; -} - -bool fromJSON(const llvm::json::Value &V, ErrorInfo &EI, llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("code", EI.code) && O.map("message", EI.message) && - O.mapOptional("data", EI.data); +bool operator==(const Request &a, const Request &b) { + return a.id == b.id && a.method == b.method && a.params == b.params; } llvm::json::Value toJSON(const Error &E) { - return json::Object{{"jsonrpc", "2.0"}, {"id", E.id}, {"error", E.error}}; + llvm::json::Object Result{{"code", E.code}, {"message", E.message}}; + if (E.data) + Result.insert({"data", *E.data}); + return Result; } bool fromJSON(const llvm::json::Value &V, Error &E, llvm::json::Path P) { llvm::json::ObjectMapper O(V, P); - return O && O.map("id", E.id) && O.map("error", E.error); + return O && O.map("code", E.code) && O.map("message", E.message) && + mapRaw(V, "data", E.data, P); +} + +bool operator==(const Error &a, const Error &b) { + return a.code == b.code && a.message == b.message && a.data == b.data; } llvm::json::Value toJSON(const Response &R) { - llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", R.id}}; - if (R.result) - Result.insert({"result", R.result}); - if (R.error) - Result.insert({"error", R.error}); + llvm::json::Object Result{{"jsonrpc", "2.0"}, {"id", toJSON(R.id)}}; + + if (const Error *error = std::get_if<Error>(&R.result)) + Result.insert({"error", *error}); + if (const json::Value *result = std::get_if<json::Value>(&R.result)) + Result.insert({"result", *result}); return Result; } bool fromJSON(const llvm::json::Value &V, Response &R, llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - if (!O || !O.map("id", R.id) || !O.map("error", R.error)) + const json::Object *E = V.getAsObject(); + if (!E) { + P.report("expected object"); + return false; + } + + const json::Value *result = E->get("result"); + const json::Value *raw_error = E->get("error"); + + if (result && raw_error) { + P.report("'result' and 'error' fields are mutually exclusive"); return false; - return mapRaw(V, "result", R.result, P); + } + + if (!result && !raw_error) { + P.report("'result' or 'error' fields are required'"); + return false; + } + + if (result) { + R.result = std::move(*result); + } else { + Error error; + if (!fromJSON(*raw_error, error, P)) + return false; + R.result = std::move(error); + } + + return mapId(V, "id", R.id, P); +} + +bool operator==(const Response &a, const Response &b) { + return a.id == b.id && a.result == b.result; } llvm::json::Value toJSON(const Notification &N) { @@ -97,30 +163,8 @@ bool fromJSON(const llvm::json::Value &V, Notification &N, llvm::json::Path P) { return true; } -llvm::json::Value toJSON(const ToolCapability &TC) { - return llvm::json::Object{{"listChanged", TC.listChanged}}; -} - -bool fromJSON(const llvm::json::Value &V, ToolCapability &TC, - llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("listChanged", TC.listChanged); -} - -llvm::json::Value toJSON(const ResourceCapability &RC) { - return llvm::json::Object{{"listChanged", RC.listChanged}, - {"subscribe", RC.subscribe}}; -} - -bool fromJSON(const llvm::json::Value &V, ResourceCapability &RC, - llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("listChanged", RC.listChanged) && - O.map("subscribe", RC.subscribe); -} - -llvm::json::Value toJSON(const Capabilities &C) { - return llvm::json::Object{{"tools", C.tools}, {"resources", C.resources}}; +bool operator==(const Notification &a, const Notification &b) { + return a.method == b.method && a.params == b.params; } bool fromJSON(const llvm::json::Value &V, Resource &R, llvm::json::Path P) { @@ -139,30 +183,25 @@ llvm::json::Value toJSON(const Resource &R) { return Result; } -bool fromJSON(const llvm::json::Value &V, Capabilities &C, llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("tools", C.tools); -} - -llvm::json::Value toJSON(const ResourceContents &RC) { +llvm::json::Value toJSON(const TextResourceContents &RC) { llvm::json::Object Result{{"uri", RC.uri}, {"text", RC.text}}; if (!RC.mimeType.empty()) Result.insert({"mimeType", RC.mimeType}); return Result; } -bool fromJSON(const llvm::json::Value &V, ResourceContents &RC, +bool fromJSON(const llvm::json::Value &V, TextResourceContents &RC, llvm::json::Path P) { llvm::json::ObjectMapper O(V, P); return O && O.map("uri", RC.uri) && O.map("text", RC.text) && O.mapOptional("mimeType", RC.mimeType); } -llvm::json::Value toJSON(const ResourceResult &RR) { +llvm::json::Value toJSON(const ReadResourceResult &RR) { return llvm::json::Object{{"contents", RR.contents}}; } -bool fromJSON(const llvm::json::Value &V, ResourceResult &RR, +bool fromJSON(const llvm::json::Value &V, ReadResourceResult &RR, llvm::json::Path P) { llvm::json::ObjectMapper O(V, P); return O && O.map("contents", RR.contents); @@ -177,15 +216,6 @@ bool fromJSON(const llvm::json::Value &V, TextContent &TC, llvm::json::Path P) { return O && O.map("text", TC.text); } -llvm::json::Value toJSON(const TextResult &TR) { - return llvm::json::Object{{"content", TR.content}, {"isError", TR.isError}}; -} - -bool fromJSON(const llvm::json::Value &V, TextResult &TR, llvm::json::Path P) { - llvm::json::ObjectMapper O(V, P); - return O && O.map("content", TR.content) && O.map("isError", TR.isError); -} - llvm::json::Value toJSON(const ToolDefinition &TD) { llvm::json::Object Result{{"name", TD.name}}; if (!TD.description.empty()) @@ -235,24 +265,16 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) { return true; } - if (O->get("error")) { - Error E; - if (!fromJSON(V, E, P)) - return false; - M = std::move(E); - return true; - } - - if (O->get("result")) { - Response R; + if (O->get("method")) { + Request R; if (!fromJSON(V, R, P)) return false; M = std::move(R); return true; } - if (O->get("method")) { - Request R; + if (O->get("result") || O->get("error")) { + Response R; if (!fromJSON(V, R, P)) return false; M = std::move(R); @@ -263,4 +285,159 @@ bool fromJSON(const llvm::json::Value &V, Message &M, llvm::json::Path P) { return false; } +json::Value toJSON(const Implementation &I) { + json::Object result{{"name", I.name}, {"version", I.version}}; + + if (!I.title.empty()) + result.insert({"title", I.title}); + + return result; +} + +bool fromJSON(const json::Value &V, Implementation &I, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("name", I.name) && O.mapOptional("title", I.title) && + O.mapOptional("version", I.version); +} + +json::Value toJSON(const ClientCapabilities &C) { return json::Object{}; } + +bool fromJSON(const json::Value &, ClientCapabilities &, json::Path) { + return true; +} + +json::Value toJSON(const ServerCapabilities &C) { + json::Object result{}; + + if (C.supportsToolsList) + result.insert({"tools", json::Object{{"listChanged", true}}}); + + if (C.supportsResourcesList || C.supportsResourcesSubscribe) { + json::Object resources; + if (C.supportsResourcesList) + resources.insert({"listChanged", true}); + if (C.supportsResourcesSubscribe) + resources.insert({"subscribe", true}); + result.insert({"resources", std::move(resources)}); + } + + if (C.supportsCompletions) + result.insert({"completions", json::Object{}}); + + if (C.supportsLogging) + result.insert({"logging", json::Object{}}); + + return result; +} + +bool fromJSON(const json::Value &V, ServerCapabilities &C, json::Path P) { + const json::Object *O = V.getAsObject(); + if (!O) { + P.report("expected object"); + return false; + } + + if (O->find("tools") != O->end()) + C.supportsToolsList = true; + + return true; +} + +json::Value toJSON(const InitializeParams &P) { + return json::Object{ + {"protocolVersion", P.protocolVersion}, + {"capabilities", P.capabilities}, + {"clientInfo", P.clientInfo}, + }; +} + +bool fromJSON(const json::Value &V, InitializeParams &I, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("protocolVersion", I.protocolVersion) && + O.map("capabilities", I.capabilities) && + O.map("clientInfo", I.clientInfo); +} + +json::Value toJSON(const InitializeResult &R) { + json::Object result{{"protocolVersion", R.protocolVersion}, + {"capabilities", R.capabilities}, + {"serverInfo", R.serverInfo}}; + + if (!R.instructions.empty()) + result.insert({"instructions", R.instructions}); + + return result; +} + +bool fromJSON(const json::Value &V, InitializeResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("protocolVersion", R.protocolVersion) && + O.map("capabilities", R.capabilities) && + O.map("serverInfo", R.serverInfo) && + O.mapOptional("instructions", R.instructions); +} + +json::Value toJSON(const ListToolsResult &R) { + return json::Object{{"tools", R.tools}}; +} + +bool fromJSON(const json::Value &V, ListToolsResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("tools", R.tools); +} + +json::Value toJSON(const CallToolResult &R) { + json::Object result{{"content", R.content}}; + + if (R.isError) + result.insert({"isError", R.isError}); + if (R.structuredContent) + result.insert({"structuredContent", *R.structuredContent}); + + return result; +} + +bool fromJSON(const json::Value &V, CallToolResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("content", R.content) && + O.mapOptional("isError", R.isError) && + mapRaw(V, "structuredContent", R.structuredContent, P); +} + +json::Value toJSON(const CallToolParams &R) { + json::Object result{{"name", R.name}}; + + if (R.arguments) + result.insert({"arguments", *R.arguments}); + + return result; +} + +bool fromJSON(const json::Value &V, CallToolParams &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("name", R.name) && mapRaw(V, "arguments", R.arguments, P); +} + +json::Value toJSON(const ReadResourceParams &R) { + return json::Object{{"uri", R.uri}}; +} + +bool fromJSON(const json::Value &V, ReadResourceParams &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("uri", R.uri); +} + +json::Value toJSON(const ListResourcesResult &R) { + return json::Object{{"resources", R.resources}}; +} + +bool fromJSON(const json::Value &V, ListResourcesResult &R, json::Path P) { + json::ObjectMapper O(V, P); + return O && O.map("resources", R.resources); +} + +json::Value toJSON(const Void &R) { return json::Object{}; } + +bool fromJSON(const json::Value &V, Void &R, json::Path P) { return true; } + } // namespace lldb_protocol::mcp diff --git a/lldb/source/Protocol/MCP/Server.cpp b/lldb/source/Protocol/MCP/Server.cpp index 4ec127fe..0381b7f 100644 --- a/lldb/source/Protocol/MCP/Server.cpp +++ b/lldb/source/Protocol/MCP/Server.cpp @@ -8,12 +8,29 @@ #include "lldb/Protocol/MCP/Server.h" #include "lldb/Protocol/MCP/MCPError.h" +#include "lldb/Protocol/MCP/Protocol.h" +#include "llvm/Support/JSON.h" using namespace lldb_protocol::mcp; using namespace llvm; -Server::Server(std::string name, std::string version) - : m_name(std::move(name)), m_version(std::move(version)) { +llvm::json::Value lldb_protocol::mcp::toJSON(const ServerInfo &SM) { + return llvm::json::Object{{"connection_uri", SM.connection_uri}, + {"pid", SM.pid}}; +} + +bool lldb_protocol::mcp::fromJSON(const llvm::json::Value &V, ServerInfo &SM, + llvm::json::Path P) { + llvm::json::ObjectMapper O(V, P); + return O && O.map("connection_uri", SM.connection_uri) && + O.map("pid", SM.pid); +} + +Server::Server(std::string name, std::string version, + std::unique_ptr<MCPTransport> transport_up, + lldb_private::MainLoop &loop) + : m_name(std::move(name)), m_version(std::move(version)), + m_transport_up(std::move(transport_up)), m_loop(loop) { AddRequestHandlers(); } @@ -30,7 +47,7 @@ void Server::AddRequestHandlers() { this, std::placeholders::_1)); } -llvm::Expected<Response> Server::Handle(Request request) { +llvm::Expected<Response> Server::Handle(const Request &request) { auto it = m_request_handlers.find(request.method); if (it != m_request_handlers.end()) { llvm::Expected<Response> response = it->second(request); @@ -44,7 +61,7 @@ llvm::Expected<Response> Server::Handle(Request request) { llvm::formatv("no handler for request: {0}", request.method).str()); } -void Server::Handle(Notification notification) { +void Server::Handle(const Notification ¬ification) { auto it = m_notification_handlers.find(notification.method); if (it != m_notification_handlers.end()) { it->second(notification); @@ -52,50 +69,7 @@ void Server::Handle(Notification notification) { } } -llvm::Expected<std::optional<Message>> -Server::HandleData(llvm::StringRef data) { - auto message = llvm::json::parse<Message>(/*JSON=*/data); - if (!message) - return message.takeError(); - - if (const Request *request = std::get_if<Request>(&(*message))) { - llvm::Expected<Response> response = Handle(*request); - - // Handle failures by converting them into an Error message. - if (!response) { - Error protocol_error; - llvm::handleAllErrors( - response.takeError(), - [&](const MCPError &err) { protocol_error = err.toProtcolError(); }, - [&](const llvm::ErrorInfoBase &err) { - protocol_error.error.code = MCPError::kInternalError; - protocol_error.error.message = err.message(); - }); - protocol_error.id = request->id; - return protocol_error; - } - - return *response; - } - - if (const Notification *notification = - std::get_if<Notification>(&(*message))) { - Handle(*notification); - return std::nullopt; - } - - if (std::get_if<Error>(&(*message))) - return llvm::createStringError("unexpected MCP message: error"); - - if (std::get_if<Response>(&(*message))) - return llvm::createStringError("unexpected MCP message: response"); - - llvm_unreachable("all message types handled"); -} - void Server::AddTool(std::unique_ptr<Tool> tool) { - std::lock_guard<std::mutex> guard(m_mutex); - if (!tool) return; m_tools[tool->GetName()] = std::move(tool); @@ -103,42 +77,39 @@ void Server::AddTool(std::unique_ptr<Tool> tool) { void Server::AddResourceProvider( std::unique_ptr<ResourceProvider> resource_provider) { - std::lock_guard<std::mutex> guard(m_mutex); - if (!resource_provider) return; m_resource_providers.push_back(std::move(resource_provider)); } void Server::AddRequestHandler(llvm::StringRef method, RequestHandler handler) { - std::lock_guard<std::mutex> guard(m_mutex); m_request_handlers[method] = std::move(handler); } void Server::AddNotificationHandler(llvm::StringRef method, NotificationHandler handler) { - std::lock_guard<std::mutex> guard(m_mutex); m_notification_handlers[method] = std::move(handler); } llvm::Expected<Response> Server::InitializeHandler(const Request &request) { Response response; - response.result.emplace(llvm::json::Object{ - {"protocolVersion", mcp::kProtocolVersion}, - {"capabilities", GetCapabilities()}, - {"serverInfo", - llvm::json::Object{{"name", m_name}, {"version", m_version}}}}); + InitializeResult result; + result.protocolVersion = mcp::kProtocolVersion; + result.capabilities = GetCapabilities(); + result.serverInfo.name = m_name; + result.serverInfo.version = m_version; + response.result = std::move(result); return response; } llvm::Expected<Response> Server::ToolsListHandler(const Request &request) { Response response; - llvm::json::Array tools; + ListToolsResult result; for (const auto &tool : m_tools) - tools.emplace_back(toJSON(tool.second->GetDefinition())); + result.tools.emplace_back(tool.second->GetDefinition()); - response.result.emplace(llvm::json::Object{{"tools", std::move(tools)}}); + response.result = std::move(result); return response; } @@ -148,16 +119,12 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) { if (!request.params) return llvm::createStringError("no tool parameters"); + CallToolParams params; + json::Path::Root root("params"); + if (!fromJSON(request.params, params, root)) + return root.getError(); - const json::Object *param_obj = request.params->getAsObject(); - if (!param_obj) - return llvm::createStringError("no tool parameters"); - - const json::Value *name = param_obj->get("name"); - if (!name) - return llvm::createStringError("no tool name"); - - llvm::StringRef tool_name = name->getAsString().value_or(""); + llvm::StringRef tool_name = params.name; if (tool_name.empty()) return llvm::createStringError("no tool name"); @@ -166,14 +133,14 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) { return llvm::createStringError(llvm::formatv("no tool \"{0}\"", tool_name)); ToolArguments tool_args; - if (const json::Value *args = param_obj->get("arguments")) - tool_args = *args; + if (params.arguments) + tool_args = *params.arguments; - llvm::Expected<TextResult> text_result = it->second->Call(tool_args); + llvm::Expected<CallToolResult> text_result = it->second->Call(tool_args); if (!text_result) return text_result.takeError(); - response.result.emplace(toJSON(*text_result)); + response.result = toJSON(*text_result); return response; } @@ -181,16 +148,13 @@ llvm::Expected<Response> Server::ToolsCallHandler(const Request &request) { llvm::Expected<Response> Server::ResourcesListHandler(const Request &request) { Response response; - llvm::json::Array resources; - - std::lock_guard<std::mutex> guard(m_mutex); + ListResourcesResult result; for (std::unique_ptr<ResourceProvider> &resource_provider_up : - m_resource_providers) { + m_resource_providers) for (const Resource &resource : resource_provider_up->GetResources()) - resources.push_back(resource); - } - response.result.emplace( - llvm::json::Object{{"resources", std::move(resources)}}); + result.resources.push_back(resource); + + response.result = std::move(result); return response; } @@ -201,22 +165,18 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) { if (!request.params) return llvm::createStringError("no resource parameters"); - const json::Object *param_obj = request.params->getAsObject(); - if (!param_obj) - return llvm::createStringError("no resource parameters"); - - const json::Value *uri = param_obj->get("uri"); - if (!uri) - return llvm::createStringError("no resource uri"); + ReadResourceParams params; + json::Path::Root root("params"); + if (!fromJSON(request.params, params, root)) + return root.getError(); - llvm::StringRef uri_str = uri->getAsString().value_or(""); + llvm::StringRef uri_str = params.uri; if (uri_str.empty()) return llvm::createStringError("no resource uri"); - std::lock_guard<std::mutex> guard(m_mutex); for (std::unique_ptr<ResourceProvider> &resource_provider_up : m_resource_providers) { - llvm::Expected<ResourceResult> result = + llvm::Expected<ReadResourceResult> result = resource_provider_up->ReadResource(uri_str); if (result.errorIsA<UnsupportedURI>()) { llvm::consumeError(result.takeError()); @@ -226,7 +186,7 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) { return result.takeError(); Response response; - response.result.emplace(std::move(*result)); + response.result = std::move(*result); return response; } @@ -234,3 +194,71 @@ llvm::Expected<Response> Server::ResourcesReadHandler(const Request &request) { llvm::formatv("no resource handler for uri: {0}", uri_str).str(), MCPError::kResourceNotFound); } + +ServerCapabilities Server::GetCapabilities() { + lldb_protocol::mcp::ServerCapabilities capabilities; + capabilities.supportsToolsList = true; + // FIXME: Support sending notifications when a debugger/target are + // added/removed. + capabilities.supportsResourcesList = false; + return capabilities; +} + +llvm::Error Server::Run() { + auto handle = m_transport_up->RegisterMessageHandler(m_loop, *this); + if (!handle) + return handle.takeError(); + + lldb_private::Status status = m_loop.Run(); + if (status.Fail()) + return status.takeError(); + + return llvm::Error::success(); +} + +void Server::Received(const Request &request) { + auto SendResponse = [this](const Response &response) { + if (llvm::Error error = m_transport_up->Send(response)) + m_transport_up->Log(llvm::toString(std::move(error))); + }; + + llvm::Expected<Response> response = Handle(request); + if (response) + return SendResponse(*response); + + lldb_protocol::mcp::Error protocol_error; + llvm::handleAllErrors( + response.takeError(), + [&](const MCPError &err) { protocol_error = err.toProtocolError(); }, + [&](const llvm::ErrorInfoBase &err) { + protocol_error.code = MCPError::kInternalError; + protocol_error.message = err.message(); + }); + Response error_response; + error_response.id = request.id; + error_response.result = std::move(protocol_error); + SendResponse(error_response); +} + +void Server::Received(const Response &response) { + m_transport_up->Log("unexpected MCP message: response"); +} + +void Server::Received(const Notification ¬ification) { + Handle(notification); +} + +void Server::OnError(llvm::Error error) { + m_transport_up->Log(llvm::toString(std::move(error))); + TerminateLoop(); +} + +void Server::OnClosed() { + m_transport_up->Log("EOF"); + TerminateLoop(); +} + +void Server::TerminateLoop() { + m_loop.AddPendingCallback( + [](lldb_private::MainLoopBase &loop) { loop.RequestTermination(); }); +} diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp index 21daf74..7efce2a 100644 --- a/lldb/source/Symbol/ObjectFile.cpp +++ b/lldb/source/Symbol/ObjectFile.cpp @@ -379,6 +379,7 @@ AddressClass ObjectFile::GetAddressClass(addr_t file_addr) { case eSectionTypeELFDynamicSymbols: case eSectionTypeELFRelocationEntries: case eSectionTypeELFDynamicLinkInfo: + case eSectionTypeWasmName: case eSectionTypeOther: return AddressClass::eUnknown; case eSectionTypeAbsoluteAddress: diff --git a/lldb/source/Symbol/Symbol.cpp b/lldb/source/Symbol/Symbol.cpp index d6689a6..40497db 100644 --- a/lldb/source/Symbol/Symbol.cpp +++ b/lldb/source/Symbol/Symbol.cpp @@ -392,45 +392,8 @@ bool Symbol::Compare(ConstString name, SymbolType type) const { return false; } -#define ENUM_TO_CSTRING(x) \ - case eSymbolType##x: \ - return #x; - const char *Symbol::GetTypeAsString() const { - switch (m_type) { - ENUM_TO_CSTRING(Invalid); - ENUM_TO_CSTRING(Absolute); - ENUM_TO_CSTRING(Code); - ENUM_TO_CSTRING(Resolver); - ENUM_TO_CSTRING(Data); - ENUM_TO_CSTRING(Trampoline); - ENUM_TO_CSTRING(Runtime); - ENUM_TO_CSTRING(Exception); - ENUM_TO_CSTRING(SourceFile); - ENUM_TO_CSTRING(HeaderFile); - ENUM_TO_CSTRING(ObjectFile); - ENUM_TO_CSTRING(CommonBlock); - ENUM_TO_CSTRING(Block); - ENUM_TO_CSTRING(Local); - ENUM_TO_CSTRING(Param); - ENUM_TO_CSTRING(Variable); - ENUM_TO_CSTRING(VariableType); - ENUM_TO_CSTRING(LineEntry); - ENUM_TO_CSTRING(LineHeader); - ENUM_TO_CSTRING(ScopeBegin); - ENUM_TO_CSTRING(ScopeEnd); - ENUM_TO_CSTRING(Additional); - ENUM_TO_CSTRING(Compiler); - ENUM_TO_CSTRING(Instrumentation); - ENUM_TO_CSTRING(Undefined); - ENUM_TO_CSTRING(ObjCClass); - ENUM_TO_CSTRING(ObjCMetaClass); - ENUM_TO_CSTRING(ObjCIVar); - ENUM_TO_CSTRING(ReExported); - default: - break; - } - return "<unknown SymbolType>"; + return GetTypeAsString(static_cast<lldb::SymbolType>(m_type)); } void Symbol::CalculateSymbolContext(SymbolContext *sc) { @@ -774,6 +737,79 @@ bool Symbol::operator==(const Symbol &rhs) const { return true; } +#define ENUM_TO_CSTRING(x) \ + case eSymbolType##x: \ + return #x; + +const char *Symbol::GetTypeAsString(lldb::SymbolType symbol_type) { + switch (symbol_type) { + ENUM_TO_CSTRING(Invalid); + ENUM_TO_CSTRING(Absolute); + ENUM_TO_CSTRING(Code); + ENUM_TO_CSTRING(Resolver); + ENUM_TO_CSTRING(Data); + ENUM_TO_CSTRING(Trampoline); + ENUM_TO_CSTRING(Runtime); + ENUM_TO_CSTRING(Exception); + ENUM_TO_CSTRING(SourceFile); + ENUM_TO_CSTRING(HeaderFile); + ENUM_TO_CSTRING(ObjectFile); + ENUM_TO_CSTRING(CommonBlock); + ENUM_TO_CSTRING(Block); + ENUM_TO_CSTRING(Local); + ENUM_TO_CSTRING(Param); + ENUM_TO_CSTRING(Variable); + ENUM_TO_CSTRING(VariableType); + ENUM_TO_CSTRING(LineEntry); + ENUM_TO_CSTRING(LineHeader); + ENUM_TO_CSTRING(ScopeBegin); + ENUM_TO_CSTRING(ScopeEnd); + ENUM_TO_CSTRING(Additional); + ENUM_TO_CSTRING(Compiler); + ENUM_TO_CSTRING(Instrumentation); + ENUM_TO_CSTRING(Undefined); + ENUM_TO_CSTRING(ObjCClass); + ENUM_TO_CSTRING(ObjCMetaClass); + ENUM_TO_CSTRING(ObjCIVar); + ENUM_TO_CSTRING(ReExported); + } + return "<unknown SymbolType>"; +} + +lldb::SymbolType Symbol::GetTypeFromString(const char *str) { + std::string str_lower = llvm::StringRef(str).lower(); + return llvm::StringSwitch<lldb::SymbolType>(str_lower) + .Case("absolute", eSymbolTypeAbsolute) + .Case("code", eSymbolTypeCode) + .Case("resolver", eSymbolTypeResolver) + .Case("data", eSymbolTypeData) + .Case("trampoline", eSymbolTypeTrampoline) + .Case("runtime", eSymbolTypeRuntime) + .Case("exception", eSymbolTypeException) + .Case("sourcefile", eSymbolTypeSourceFile) + .Case("headerfile", eSymbolTypeHeaderFile) + .Case("objectfile", eSymbolTypeObjectFile) + .Case("commonblock", eSymbolTypeCommonBlock) + .Case("block", eSymbolTypeBlock) + .Case("local", eSymbolTypeLocal) + .Case("param", eSymbolTypeParam) + .Case("variable", eSymbolTypeVariable) + .Case("variableType", eSymbolTypeVariableType) + .Case("lineentry", eSymbolTypeLineEntry) + .Case("lineheader", eSymbolTypeLineHeader) + .Case("scopebegin", eSymbolTypeScopeBegin) + .Case("scopeend", eSymbolTypeScopeEnd) + .Case("additional,", eSymbolTypeAdditional) + .Case("compiler", eSymbolTypeCompiler) + .Case("instrumentation", eSymbolTypeInstrumentation) + .Case("undefined", eSymbolTypeUndefined) + .Case("objcclass", eSymbolTypeObjCClass) + .Case("objcmetaclass", eSymbolTypeObjCMetaClass) + .Case("objcivar", eSymbolTypeObjCIVar) + .Case("reexported", eSymbolTypeReExported) + .Default(eSymbolTypeInvalid); +} + namespace llvm { namespace json { @@ -804,36 +840,8 @@ bool fromJSON(const llvm::json::Value &value, lldb_private::JSONSymbol &symbol, bool fromJSON(const llvm::json::Value &value, lldb::SymbolType &type, llvm::json::Path path) { if (auto str = value.getAsString()) { - type = llvm::StringSwitch<lldb::SymbolType>(*str) - .Case("absolute", eSymbolTypeAbsolute) - .Case("code", eSymbolTypeCode) - .Case("resolver", eSymbolTypeResolver) - .Case("data", eSymbolTypeData) - .Case("trampoline", eSymbolTypeTrampoline) - .Case("runtime", eSymbolTypeRuntime) - .Case("exception", eSymbolTypeException) - .Case("sourcefile", eSymbolTypeSourceFile) - .Case("headerfile", eSymbolTypeHeaderFile) - .Case("objectfile", eSymbolTypeObjectFile) - .Case("commonblock", eSymbolTypeCommonBlock) - .Case("block", eSymbolTypeBlock) - .Case("local", eSymbolTypeLocal) - .Case("param", eSymbolTypeParam) - .Case("variable", eSymbolTypeVariable) - .Case("variableType", eSymbolTypeVariableType) - .Case("lineentry", eSymbolTypeLineEntry) - .Case("lineheader", eSymbolTypeLineHeader) - .Case("scopebegin", eSymbolTypeScopeBegin) - .Case("scopeend", eSymbolTypeScopeEnd) - .Case("additional,", eSymbolTypeAdditional) - .Case("compiler", eSymbolTypeCompiler) - .Case("instrumentation", eSymbolTypeInstrumentation) - .Case("undefined", eSymbolTypeUndefined) - .Case("objcclass", eSymbolTypeObjCClass) - .Case("objcmetaClass", eSymbolTypeObjCMetaClass) - .Case("objcivar", eSymbolTypeObjCIVar) - .Case("reexporte", eSymbolTypeReExported) - .Default(eSymbolTypeInvalid); + llvm::StringRef str_ref = str.value_or(""); + type = Symbol::GetTypeFromString(str_ref.data()); if (type == eSymbolTypeInvalid) { path.report("invalid symbol type"); diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp index 3f54ea5..9d232e4 100644 --- a/lldb/source/Target/ExecutionContext.cpp +++ b/lldb/source/Target/ExecutionContext.cpp @@ -12,7 +12,9 @@ #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/State.h" +#include <mutex> using namespace lldb_private; @@ -125,19 +127,47 @@ ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr, } } -ExecutionContext::ExecutionContext(const ExecutionContextRef *exe_ctx_ref_ptr, - std::unique_lock<std::recursive_mutex> &lock) - : m_target_sp(), m_process_sp(), m_thread_sp(), m_frame_sp() { - if (exe_ctx_ref_ptr) { - m_target_sp = exe_ctx_ref_ptr->GetTargetSP(); - if (m_target_sp) { - lock = std::unique_lock<std::recursive_mutex>(m_target_sp->GetAPIMutex()); +llvm::Expected<StoppedExecutionContext> +lldb_private::GetStoppedExecutionContext( + const lldb::ExecutionContextRefSP &exe_ctx_ref_ptr) { + return GetStoppedExecutionContext(exe_ctx_ref_ptr.get()); +} - m_process_sp = exe_ctx_ref_ptr->GetProcessSP(); - m_thread_sp = exe_ctx_ref_ptr->GetThreadSP(); - m_frame_sp = exe_ctx_ref_ptr->GetFrameSP(); - } - } +llvm::Expected<StoppedExecutionContext> +lldb_private::GetStoppedExecutionContext( + const ExecutionContextRef *exe_ctx_ref_ptr) { + if (!exe_ctx_ref_ptr) + return llvm::createStringError( + "StoppedExecutionContext created with an empty ExecutionContextRef"); + + lldb::TargetSP target_sp = exe_ctx_ref_ptr->GetTargetSP(); + if (!target_sp) + return llvm::createStringError( + "StoppedExecutionContext created with a null target"); + + auto api_lock = + std::unique_lock<std::recursive_mutex>(target_sp->GetAPIMutex()); + + auto process_sp = exe_ctx_ref_ptr->GetProcessSP(); + if (!process_sp) + return llvm::createStringError( + "StoppedExecutionContext created with a null process"); + + ProcessRunLock::ProcessRunLocker stop_locker; + if (!stop_locker.TryLock(&process_sp->GetRunLock())) + return llvm::createStringError( + "attempted to create a StoppedExecutionContext with a running process"); + + auto thread_sp = exe_ctx_ref_ptr->GetThreadSP(); + auto frame_sp = exe_ctx_ref_ptr->GetFrameSP(); + return StoppedExecutionContext(target_sp, process_sp, thread_sp, frame_sp, + std::move(api_lock), std::move(stop_locker)); +} + +std::unique_lock<std::recursive_mutex> StoppedExecutionContext::AllowResume() { + Clear(); + m_stop_locker = ProcessRunLock::ProcessRunLocker(); + return std::move(m_api_lock); } ExecutionContext::ExecutionContext(ExecutionContextScope *exe_scope_ptr) diff --git a/lldb/source/Target/RegisterContextUnwind.cpp b/lldb/source/Target/RegisterContextUnwind.cpp index 9e9e2d8..bcf1297 100644 --- a/lldb/source/Target/RegisterContextUnwind.cpp +++ b/lldb/source/Target/RegisterContextUnwind.cpp @@ -2026,7 +2026,6 @@ bool RegisterContextUnwind::ReadFrameAddress( "Got an invalid CFA register value - reg %s (%d), value 0x%" PRIx64, cfa_reg.GetName(), cfa_reg.GetAsKind(eRegisterKindLLDB), cfa_reg_contents); - cfa_reg_contents = LLDB_INVALID_ADDRESS; return false; } address = cfa_reg_contents + fa.GetOffset(); diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index d97a814..f27004c 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -61,8 +61,9 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(), - m_id(pc, cfa, nullptr), m_frame_code_addr(pc), m_sc(), m_flags(), - m_frame_base(), m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), + m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()), + m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), + m_frame_base_error(), m_cfa_is_valid(cfa_is_valid), m_stack_frame_kind(kind), m_behaves_like_zeroth_frame(behaves_like_zeroth_frame), m_variable_list_sp(), m_variable_list_value_objects(), @@ -71,7 +72,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, // recursive functions properly aren't confused with one another on a history // stack. if (IsHistorical() && !m_cfa_is_valid) { - m_id.SetCFA(m_frame_index); + m_id.SetCFA(m_frame_index, thread_sp->GetProcess().get()); } if (sc_ptr != nullptr) { @@ -87,7 +88,8 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, const SymbolContext *sc_ptr) : m_thread_wp(thread_sp), m_frame_index(frame_idx), m_concrete_frame_index(unwind_frame_index), - m_reg_context_sp(reg_context_sp), m_id(pc, cfa, nullptr), + m_reg_context_sp(reg_context_sp), + m_id(pc, cfa, nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), m_stack_frame_kind(StackFrame::Kind::Regular), @@ -115,7 +117,7 @@ StackFrame::StackFrame(const ThreadSP &thread_sp, user_id_t frame_idx, m_concrete_frame_index(unwind_frame_index), m_reg_context_sp(reg_context_sp), m_id(pc_addr.GetLoadAddress(thread_sp->CalculateTarget().get()), cfa, - nullptr), + nullptr, thread_sp->GetProcess().get()), m_frame_code_addr(pc_addr), m_sc(), m_flags(), m_frame_base(), m_frame_base_error(), m_cfa_is_valid(true), m_stack_frame_kind(StackFrame::Kind::Regular), @@ -264,6 +266,7 @@ bool StackFrame::ChangePC(addr_t pc) { const char *StackFrame::Disassemble() { std::lock_guard<std::recursive_mutex> guard(m_mutex); + if (!m_disassembly.Empty()) return m_disassembly.GetData(); @@ -438,10 +441,10 @@ VariableList *StackFrame::GetVariableList(bool get_file_globals, const bool get_child_variables = true; const bool can_create = true; const bool stop_if_child_block_is_inlined_function = true; - frame_block->AppendBlockVariables(can_create, get_child_variables, - stop_if_child_block_is_inlined_function, - [](Variable *v) { return true; }, - m_variable_list_sp.get()); + frame_block->AppendBlockVariables( + can_create, get_child_variables, + stop_if_child_block_is_inlined_function, + [](Variable *v) { return true; }, m_variable_list_sp.get()); } } @@ -1225,10 +1228,12 @@ StackFrame::GetValueObjectForFrameVariable(const VariableSP &variable_sp, VariableList *var_list = GetVariableList(true, nullptr); if (var_list) { // Make sure the variable is a frame variable - const uint32_t var_idx = var_list->FindIndexForVariable(variable_sp.get()); + const uint32_t var_idx = + var_list->FindIndexForVariable(variable_sp.get()); const uint32_t num_variables = var_list->GetSize(); if (var_idx < num_variables) { - valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); + valobj_sp = + m_variable_list_value_objects.GetValueObjectAtIndex(var_idx); if (!valobj_sp) { if (m_variable_list_value_objects.GetSize() < num_variables) m_variable_list_value_objects.Resize(num_variables); @@ -1762,11 +1767,9 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (clobbered_reg_matcher(operands[0])) { origin_operand = &operands[1]; - } - else if (clobbered_reg_matcher(operands[1])) { + } else if (clobbered_reg_matcher(operands[1])) { origin_operand = &operands[0]; - } - else { + } else { continue; } @@ -1792,8 +1795,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, if (!source_path) { continue; } - source_path = - GetValueForDereferincingOffset(frame, source_path, offset); + source_path = GetValueForDereferincingOffset(frame, source_path, offset); } if (source_path) { @@ -1803,7 +1805,7 @@ lldb::ValueObjectSP DoGuessValueAt(StackFrame &frame, ConstString reg, return ValueObjectSP(); } -} +} // namespace lldb::ValueObjectSP StackFrame::GuessValueForRegisterAndOffset(ConstString reg, int64_t offset) { @@ -1995,7 +1997,9 @@ void StackFrame::UpdatePreviousFrameFromCurrentFrame(StackFrame &curr_frame) { std::lock_guard<std::recursive_mutex> guard(m_mutex); assert(GetStackID() == curr_frame.GetStackID()); // TODO: remove this after some testing - m_id.SetPC(curr_frame.m_id.GetPC()); // Update the Stack ID PC value + m_id.SetPC( + curr_frame.m_id.GetPC(), + curr_frame.CalculateProcess().get()); // Update the Stack ID PC value assert(GetThread() == curr_frame.GetThread()); m_frame_index = curr_frame.m_frame_index; m_concrete_frame_index = curr_frame.m_concrete_frame_index; diff --git a/lldb/source/Target/StackID.cpp b/lldb/source/Target/StackID.cpp index 410d5b7..b179597 100644 --- a/lldb/source/Target/StackID.cpp +++ b/lldb/source/Target/StackID.cpp @@ -10,10 +10,28 @@ #include "lldb/Symbol/Block.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Process.h" #include "lldb/Utility/Stream.h" using namespace lldb_private; +StackID::StackID(lldb::addr_t pc, lldb::addr_t cfa, + SymbolContextScope *symbol_scope, Process *process) + : m_pc(pc), m_cfa(cfa), m_symbol_scope(symbol_scope) { + if (process) { + m_pc = process->FixCodeAddress(m_pc); + m_cfa = process->FixDataAddress(m_cfa); + } +} + +void StackID::SetPC(lldb::addr_t pc, Process *process) { + m_pc = process ? process->FixCodeAddress(pc) : pc; +} + +void StackID::SetCFA(lldb::addr_t cfa, Process *process) { + m_cfa = process ? process->FixDataAddress(cfa) : cfa; +} + void StackID::Dump(Stream *s) { s->Printf("StackID (pc = 0x%16.16" PRIx64 ", cfa = 0x%16.16" PRIx64 ", symbol_scope = %p", diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 4f39f60..fa98c24 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -558,10 +558,11 @@ BreakpointSP Target::CreateBreakpoint(lldb::addr_t addr, bool internal, BreakpointSP Target::CreateBreakpoint(const Address &addr, bool internal, bool hardware) { - SearchFilterSP filter_sp( - new SearchFilterForUnconstrainedSearches(shared_from_this())); - BreakpointResolverSP resolver_sp( - new BreakpointResolverAddress(nullptr, addr)); + SearchFilterSP filter_sp = + std::make_shared<SearchFilterForUnconstrainedSearches>( + shared_from_this()); + BreakpointResolverSP resolver_sp = + std::make_shared<BreakpointResolverAddress>(nullptr, addr); return CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, false); } @@ -569,10 +570,12 @@ lldb::BreakpointSP Target::CreateAddressInModuleBreakpoint(lldb::addr_t file_addr, bool internal, const FileSpec &file_spec, bool request_hardware) { - SearchFilterSP filter_sp( - new SearchFilterForUnconstrainedSearches(shared_from_this())); - BreakpointResolverSP resolver_sp(new BreakpointResolverAddress( - nullptr, file_addr, file_spec)); + SearchFilterSP filter_sp = + std::make_shared<SearchFilterForUnconstrainedSearches>( + shared_from_this()); + BreakpointResolverSP resolver_sp = + std::make_shared<BreakpointResolverAddress>(nullptr, file_addr, + file_spec); return CreateBreakpoint(filter_sp, resolver_sp, internal, request_hardware, false); } @@ -581,7 +584,8 @@ BreakpointSP Target::CreateBreakpoint( const FileSpecList *containingModules, const FileSpecList *containingSourceFiles, const char *func_name, FunctionNameType func_name_type_mask, LanguageType language, - lldb::addr_t offset, LazyBool skip_prologue, bool internal, bool hardware) { + lldb::addr_t offset, bool offset_is_insn_count, LazyBool skip_prologue, + bool internal, bool hardware) { BreakpointSP bp_sp; if (func_name) { SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList( @@ -594,7 +598,7 @@ BreakpointSP Target::CreateBreakpoint( BreakpointResolverSP resolver_sp(new BreakpointResolverName( nullptr, func_name, func_name_type_mask, language, Breakpoint::Exact, - offset, skip_prologue)); + offset, offset_is_insn_count, skip_prologue)); bp_sp = CreateBreakpoint(filter_sp, resolver_sp, internal, hardware, true); } return bp_sp; @@ -2996,6 +3000,38 @@ lldb::addr_t Target::GetBreakableLoadAddress(lldb::addr_t addr) { return arch_plugin ? arch_plugin->GetBreakableLoadAddress(addr, *this) : addr; } +llvm::Expected<lldb::DisassemblerSP> +Target::ReadInstructions(const Address &start_addr, uint32_t count, + const char *flavor_string) { + DataBufferHeap data(GetArchitecture().GetMaximumOpcodeByteSize() * count, 0); + bool force_live_memory = true; + lldb_private::Status error; + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + const size_t bytes_read = + ReadMemory(start_addr, data.GetBytes(), data.GetByteSize(), error, + force_live_memory, &load_addr); + + if (error.Fail()) + return llvm::createStringError( + error.AsCString("Target::ReadInstructions failed to read memory at %s"), + start_addr.GetLoadAddress(this)); + + const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; + if (!flavor_string || flavor_string[0] == '\0') { + // FIXME - we don't have the mechanism in place to do per-architecture + // settings. But since we know that for now we only support flavors on + // x86 & x86_64, + const llvm::Triple::ArchType arch = GetArchitecture().GetTriple().getArch(); + if (arch == llvm::Triple::x86 || arch == llvm::Triple::x86_64) + flavor_string = GetDisassemblyFlavor(); + } + + return Disassembler::DisassembleBytes( + GetArchitecture(), nullptr, flavor_string, GetDisassemblyCPU(), + GetDisassemblyFeatures(), start_addr, data.GetBytes(), bytes_read, count, + data_from_file); +} + SourceManager &Target::GetSourceManager() { if (!m_source_manager_up) m_source_manager_up = std::make_unique<SourceManager>(shared_from_this()); diff --git a/lldb/source/Utility/ArchSpec.cpp b/lldb/source/Utility/ArchSpec.cpp index 7c71aaa..8f14cf6 100644 --- a/lldb/source/Utility/ArchSpec.cpp +++ b/lldb/source/Utility/ArchSpec.cpp @@ -1439,6 +1439,10 @@ bool lldb_private::operator==(const ArchSpec &lhs, const ArchSpec &rhs) { return lhs.GetCore() == rhs.GetCore(); } +bool lldb_private::operator!=(const ArchSpec &lhs, const ArchSpec &rhs) { + return !(lhs == rhs); +} + bool ArchSpec::IsFullySpecifiedTriple() const { if (!TripleOSWasSpecified()) return false; diff --git a/lldb/source/Utility/XcodeSDK.cpp b/lldb/source/Utility/XcodeSDK.cpp index eb2047e..2040791 100644 --- a/lldb/source/Utility/XcodeSDK.cpp +++ b/lldb/source/Utility/XcodeSDK.cpp @@ -243,29 +243,6 @@ bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type sdk_type, return false; } -bool XcodeSDK::SupportsSwift() const { - XcodeSDK::Info info = Parse(); - switch (info.type) { - case Type::MacOSX: - return info.version.empty() || info.version >= llvm::VersionTuple(10, 10); - case Type::iPhoneOS: - case Type::iPhoneSimulator: - return info.version.empty() || info.version >= llvm::VersionTuple(8); - case Type::AppleTVSimulator: - case Type::AppleTVOS: - return info.version.empty() || info.version >= llvm::VersionTuple(9); - case Type::WatchSimulator: - case Type::watchOS: - return info.version.empty() || info.version >= llvm::VersionTuple(2); - case Type::XROS: - case Type::XRSimulator: - case Type::Linux: - return true; - default: - return false; - } -} - bool XcodeSDK::SDKSupportsModules(XcodeSDK::Type desired_type, const FileSpec &sdk_path) { ConstString last_path_component = sdk_path.GetFilename(); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index b1cd824..7056466 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -37,4 +37,13 @@ BitFieldExtractionNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected<lldb::ValueObjectSP> +IntegerLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + +llvm::Expected<lldb::ValueObjectSP> FloatLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 6f28434..c6cf41e 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -7,7 +7,9 @@ //===----------------------------------------------------------------------===// #include "lldb/ValueObject/DILEval.h" +#include "lldb/Core/Module.h" #include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/TypeSystem.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/RegisterContext.h" #include "lldb/ValueObject/DILAST.h" @@ -330,40 +332,135 @@ Interpreter::Visit(const ArraySubscriptNode *node) { return lhs_or_err; lldb::ValueObjectSP base = *lhs_or_err; - // Check to see if 'base' has a synthetic value; if so, try using that. + StreamString var_expr_path_strm; uint64_t child_idx = node->GetIndex(); - if (lldb::ValueObjectSP synthetic = base->GetSyntheticValue()) { - llvm::Expected<uint32_t> num_children = - synthetic->GetNumChildren(child_idx + 1); - if (!num_children) - return llvm::make_error<DILDiagnosticError>( - m_expr, toString(num_children.takeError()), node->GetLocation()); - if (child_idx >= *num_children) { - std::string message = llvm::formatv( - "array index {0} is not valid for \"({1}) {2}\"", child_idx, + lldb::ValueObjectSP child_valobj_sp; + + bool is_incomplete_array = false; + CompilerType base_type = base->GetCompilerType().GetNonReferenceType(); + base->GetExpressionPath(var_expr_path_strm); + + if (base_type.IsPointerType()) { + bool is_objc_pointer = true; + + if (base->GetCompilerType().GetMinimumLanguage() != lldb::eLanguageTypeObjC) + is_objc_pointer = false; + else if (!base->GetCompilerType().IsPointerType()) + is_objc_pointer = false; + + if (!m_use_synthetic && is_objc_pointer) { + std::string err_msg = llvm::formatv( + "\"({0}) {1}\" is an Objective-C pointer, and cannot be subscripted", base->GetTypeName().AsCString("<invalid type>"), - base->GetName().AsCString()); - return llvm::make_error<DILDiagnosticError>(m_expr, message, + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), node->GetLocation()); } - if (lldb::ValueObjectSP child_valobj_sp = - synthetic->GetChildAtIndex(child_idx)) + if (is_objc_pointer) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (!synthetic || synthetic == base) { + std::string err_msg = + llvm::formatv("\"({0}) {1}\" is not an array type", + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + if (static_cast<uint32_t>(child_idx) >= + synthetic->GetNumChildrenIgnoringErrors()) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + child_valobj_sp = synthetic->GetChildAtIndex(child_idx); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + if (m_use_dynamic != lldb::eNoDynamicValues) { + if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) + child_valobj_sp = std::move(dynamic_sp); + } return child_valobj_sp; - } + } - auto base_type = base->GetCompilerType().GetNonReferenceType(); - if (!base_type.IsPointerType() && !base_type.IsArrayType()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscripted value is not an array or pointer", - node->GetLocation()); - if (base_type.IsPointerToVoid()) - return llvm::make_error<DILDiagnosticError>( - m_expr, "subscript of pointer to incomplete type 'void'", - node->GetLocation()); + child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "failed to use pointer as array for index {0} for " + "\"({1}) {2}\"", + child_idx, base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + if (base_type.IsPointerToVoid()) + err_msg = "subscript of pointer to incomplete type 'void'"; + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + } else if (base_type.IsArrayType(nullptr, nullptr, &is_incomplete_array)) { + child_valobj_sp = base->GetChildAtIndex(child_idx); + if (!child_valobj_sp && (is_incomplete_array || m_use_synthetic)) + child_valobj_sp = base->GetSyntheticArrayMember(child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation()); + } + } else if (base_type.IsScalarType()) { + child_valobj_sp = + base->GetSyntheticBitFieldChild(child_idx, child_idx, true); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "bitfield range {0}-{1} is not valid for \"({2}) {3}\"", child_idx, + child_idx, base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + } else { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (!m_use_synthetic || !synthetic || synthetic == base) { + std::string err_msg = + llvm::formatv("\"{0}\" is not an array type", + base->GetTypeName().AsCString("<invalid type>")); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + if (static_cast<uint32_t>(child_idx) >= + synthetic->GetNumChildrenIgnoringErrors(child_idx + 1)) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + child_valobj_sp = synthetic->GetChildAtIndex(child_idx); + if (!child_valobj_sp) { + std::string err_msg = llvm::formatv( + "array index {0} is not valid for \"({1}) {2}\"", child_idx, + base->GetTypeName().AsCString("<invalid type>"), + var_expr_path_strm.GetData()); + return llvm::make_error<DILDiagnosticError>(m_expr, std::move(err_msg), + node->GetLocation(), 1); + } + } - if (base_type.IsArrayType()) { - if (lldb::ValueObjectSP child_valobj_sp = base->GetChildAtIndex(child_idx)) - return child_valobj_sp; + if (child_valobj_sp) { + if (m_use_dynamic != lldb::eNoDynamicValues) { + if (auto dynamic_sp = child_valobj_sp->GetDynamicValue(m_use_dynamic)) + child_valobj_sp = std::move(dynamic_sp); + } + return child_valobj_sp; } int64_t signed_child_idx = node->GetIndex(); @@ -402,4 +499,107 @@ Interpreter::Visit(const BitFieldExtractionNode *node) { return child_valobj_sp; } +static llvm::Expected<lldb::TypeSystemSP> +GetTypeSystemFromCU(std::shared_ptr<StackFrame> ctx) { + SymbolContext symbol_context = + ctx->GetSymbolContext(lldb::eSymbolContextCompUnit); + lldb::LanguageType language = symbol_context.comp_unit->GetLanguage(); + + symbol_context = ctx->GetSymbolContext(lldb::eSymbolContextModule); + return symbol_context.module_sp->GetTypeSystemForLanguage(language); +} + +static CompilerType GetBasicType(lldb::TypeSystemSP type_system, + lldb::BasicType basic_type) { + if (type_system) + return type_system.get()->GetBasicTypeFromAST(basic_type); + + return CompilerType(); +} + +llvm::Expected<CompilerType> +Interpreter::PickIntegerType(lldb::TypeSystemSP type_system, + std::shared_ptr<ExecutionContextScope> ctx, + const IntegerLiteralNode *literal) { + // Binary, Octal, Hexadecimal and literals with a U suffix are allowed to be + // an unsigned integer. + bool unsigned_is_allowed = literal->IsUnsigned() || literal->GetRadix() != 10; + llvm::APInt apint = literal->GetValue(); + + llvm::SmallVector<std::pair<lldb::BasicType, lldb::BasicType>, 3> candidates; + if (literal->GetTypeSuffix() <= IntegerTypeSuffix::None) + candidates.emplace_back(lldb::eBasicTypeInt, + unsigned_is_allowed ? lldb::eBasicTypeUnsignedInt + : lldb::eBasicTypeInvalid); + if (literal->GetTypeSuffix() <= IntegerTypeSuffix::Long) + candidates.emplace_back(lldb::eBasicTypeLong, + unsigned_is_allowed ? lldb::eBasicTypeUnsignedLong + : lldb::eBasicTypeInvalid); + candidates.emplace_back(lldb::eBasicTypeLongLong, + lldb::eBasicTypeUnsignedLongLong); + for (auto [signed_, unsigned_] : candidates) { + CompilerType signed_type = type_system->GetBasicTypeFromAST(signed_); + if (!signed_type) + continue; + llvm::Expected<uint64_t> size = signed_type.GetBitSize(ctx.get()); + if (!size) + return size.takeError(); + if (!literal->IsUnsigned() && apint.isIntN(*size - 1)) + return signed_type; + if (unsigned_ != lldb::eBasicTypeInvalid && apint.isIntN(*size)) + return type_system->GetBasicTypeFromAST(unsigned_); + } + + return llvm::make_error<DILDiagnosticError>( + m_expr, + "integer literal is too large to be represented in any integer type", + literal->GetLocation()); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const IntegerLiteralNode *node) { + llvm::Expected<lldb::TypeSystemSP> type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + + llvm::Expected<CompilerType> type = + PickIntegerType(*type_system, m_exe_ctx_scope, node); + if (!type) + return type.takeError(); + + Scalar scalar = node->GetValue(); + // APInt from StringRef::getAsInteger comes with just enough bitwidth to + // hold the value. This adjusts APInt bitwidth to match the compiler type. + llvm::Expected<uint64_t> type_bitsize = + type->GetBitSize(m_exe_ctx_scope.get()); + if (!type_bitsize) + return type_bitsize.takeError(); + scalar.TruncOrExtendTo(*type_bitsize, false); + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, *type, + "result"); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const FloatLiteralNode *node) { + llvm::Expected<lldb::TypeSystemSP> type_system = + GetTypeSystemFromCU(m_exe_ctx_scope); + if (!type_system) + return type_system.takeError(); + + bool isFloat = + &node->GetValue().getSemantics() == &llvm::APFloat::IEEEsingle(); + lldb::BasicType basic_type = + isFloat ? lldb::eBasicTypeFloat : lldb::eBasicTypeDouble; + CompilerType type = GetBasicType(*type_system, basic_type); + + if (!type) + return llvm::make_error<DILDiagnosticError>( + m_expr, "unable to create a const literal", node->GetLocation()); + + Scalar scalar = node->GetValue(); + return ValueObject::CreateValueObjectFromScalar(m_target, scalar, type, + "result"); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index eaefaf4..0b2288a 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -28,18 +28,23 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "coloncolon"; case Kind::eof: return "eof"; + case Kind::float_constant: + return "float_constant"; case Kind::identifier: return "identifier"; + case Kind::integer_constant: + return "integer_constant"; case Kind::l_paren: return "l_paren"; case Kind::l_square: return "l_square"; case Kind::minus: return "minus"; - case Kind::numeric_constant: - return "numeric_constant"; case Kind::period: return "period"; + return "l_square"; + case Kind::plus: + return "plus"; case Kind::r_paren: return "r_paren"; case Kind::r_square: @@ -70,13 +75,32 @@ static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr, return candidate; } -static bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); } +static bool IsNumberBodyChar(char ch) { + return IsDigit(ch) || IsLetter(ch) || ch == '.'; +} -static std::optional<llvm::StringRef> IsNumber(llvm::StringRef expr, - llvm::StringRef &remainder) { - if (IsDigit(remainder[0])) { - llvm::StringRef number = remainder.take_while(IsNumberBodyChar); - remainder = remainder.drop_front(number.size()); +static std::optional<llvm::StringRef> IsNumber(llvm::StringRef &remainder, + bool &isFloat) { + llvm::StringRef tail = remainder; + llvm::StringRef body = tail.take_while(IsNumberBodyChar); + size_t dots = body.count('.'); + if (dots > 1 || dots == body.size()) + return std::nullopt; + if (IsDigit(body.front()) || (body[0] == '.' && IsDigit(body[1]))) { + isFloat = dots == 1; + tail = tail.drop_front(body.size()); + bool isHex = body.contains_insensitive('x'); + bool hasExp = !isHex && body.contains_insensitive('e'); + bool hasHexExp = isHex && body.contains_insensitive('p'); + if (hasExp || hasHexExp) { + isFloat = true; // This marks numbers like 0x1p1 and 1e1 as float + if (body.ends_with_insensitive("e") || body.ends_with_insensitive("p")) + if (tail.consume_front("+") || tail.consume_front("-")) + tail = tail.drop_while(IsNumberBodyChar); + } + size_t number_length = remainder.size() - tail.size(); + llvm::StringRef number = remainder.take_front(number_length); + remainder = remainder.drop_front(number_length); return number; } return std::nullopt; @@ -106,18 +130,21 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); - std::optional<llvm::StringRef> maybe_number = IsNumber(expr, remainder); - if (maybe_number) - return Token(Token::numeric_constant, maybe_number->str(), position); + bool isFloat = false; + std::optional<llvm::StringRef> maybe_number = IsNumber(remainder, isFloat); + if (maybe_number) { + auto kind = isFloat ? Token::float_constant : Token::integer_constant; + return Token(kind, maybe_number->str(), position); + } std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair<Token::Kind, const char *> operators[] = { - {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, - {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, - {Token::period, "."}, {Token::r_paren, ")"}, {Token::r_square, "]"}, - {Token::star, "*"}, + {Token::amp, "&"}, {Token::arrow, "->"}, {Token::coloncolon, "::"}, + {Token::l_paren, "("}, {Token::l_square, "["}, {Token::minus, "-"}, + {Token::period, "."}, {Token::plus, "+"}, {Token::r_paren, ")"}, + {Token::r_square, "]"}, {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index eac41fa..8c4f7fd 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -179,10 +179,13 @@ ASTNodeUP DILParser::ParsePostfixExpression() { // Parse a primary_expression. // // primary_expression: +// numeric_literal // id_expression // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().IsOneOf({Token::integer_constant, Token::float_constant})) + return ParseNumericLiteral(); if (CurToken().IsOneOf( {Token::coloncolon, Token::identifier, Token::l_paren})) { // Save the source location for the diagnostics message. @@ -346,6 +349,7 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// FIXME: Remove this once subscript operator uses ScalarLiteralNode. // Parse a integer_literal. // // integer_literal: @@ -370,6 +374,69 @@ std::optional<int64_t> DILParser::ParseIntegerConstant() { return std::nullopt; } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::integer_constant ? +// ? Token::floating_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + ASTNodeUP numeric_constant; + if (CurToken().Is(Token::integer_constant)) + numeric_constant = ParseIntegerLiteral(); + else + numeric_constant = ParseFloatingPointLiteral(); + if (!numeric_constant) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +ASTNodeUP DILParser::ParseIntegerLiteral() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + + auto radix = llvm::getAutoSenseRadix(spelling_ref); + IntegerTypeSuffix type = IntegerTypeSuffix::None; + bool is_unsigned = false; + if (spelling_ref.consume_back_insensitive("u")) + is_unsigned = true; + if (spelling_ref.consume_back_insensitive("ll")) + type = IntegerTypeSuffix::LongLong; + else if (spelling_ref.consume_back_insensitive("l")) + type = IntegerTypeSuffix::Long; + // Suffix 'u' can be only specified only once, before or after 'l' + if (!is_unsigned && spelling_ref.consume_back_insensitive("u")) + is_unsigned = true; + + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(radix, raw_value)) + return std::make_unique<IntegerLiteralNode>(token.GetLocation(), raw_value, + radix, is_unsigned, type); + return nullptr; +} + +ASTNodeUP DILParser::ParseFloatingPointLiteral() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + + llvm::APFloat raw_float(llvm::APFloat::IEEEdouble()); + if (spelling_ref.consume_back_insensitive("f")) + raw_float = llvm::APFloat(llvm::APFloat::IEEEsingle()); + + auto StatusOrErr = raw_float.convertFromString( + spelling_ref, llvm::APFloat::rmNearestTiesToEven); + if (!errorToBool(StatusOrErr.takeError())) + return std::make_unique<FloatLiteralNode>(token.GetLocation(), raw_float); + return nullptr; +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), |