| //===-- BreakpointLocation.cpp --------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Breakpoint/BreakpointLocation.h" | |
| #include "lldb/Breakpoint/BreakpointID.h" | |
| #include "lldb/Breakpoint/BreakpointResolver.h" | |
| #include "lldb/Breakpoint/BreakpointResolverScripted.h" | |
| #include "lldb/Breakpoint/StoppointCallbackContext.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Expression/DiagnosticManager.h" | |
| #include "lldb/Expression/ExpressionVariable.h" | |
| #include "lldb/Expression/UserExpression.h" | |
| #include "lldb/Symbol/CompileUnit.h" | |
| #include "lldb/Symbol/Symbol.h" | |
| #include "lldb/Symbol/TypeSystem.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Target/Thread.h" | |
| #include "lldb/Target/ThreadSpec.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "lldb/ValueObject/DILEval.h" | |
| #include "lldb/ValueObject/DILLexer.h" | |
| #include "lldb/ValueObject/DILParser.h" | |
| #include "lldb/ValueObject/ValueObject.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner, | |
| const Address &addr, lldb::tid_t tid, | |
| bool check_for_resolver) | |
| : m_address(addr), m_owner(owner), m_loc_id(loc_id) { | |
| if (check_for_resolver) { | |
| const Symbol *symbol = m_address.CalculateSymbolContextSymbol(); | |
| if (symbol && symbol->IsIndirect()) { | |
| SetShouldResolveIndirectFunctions(true); | |
| } | |
| } | |
| SetThreadIDInternal(tid); | |
| } | |
| BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner) | |
| : m_owner(owner), m_loc_id(loc_id) { | |
| SetThreadIDInternal(LLDB_INVALID_THREAD_ID); | |
| } | |
| BreakpointLocation::~BreakpointLocation() { | |
| llvm::consumeError(ClearBreakpointSite()); | |
| } | |
| lldb::addr_t BreakpointLocation::GetLoadAddress() const { | |
| return m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()); | |
| } | |
| const BreakpointOptions &BreakpointLocation::GetOptionsSpecifyingKind( | |
| BreakpointOptions::OptionKind kind) const { | |
| if (m_options_up && m_options_up->IsOptionSet(kind)) | |
| return *m_options_up; | |
| return m_owner.GetOptions(); | |
| } | |
| Address &BreakpointLocation::GetAddress() { return m_address; } | |
| Breakpoint &BreakpointLocation::GetBreakpoint() { return m_owner; } | |
| Target &BreakpointLocation::GetTarget() { return m_owner.GetTarget(); } | |
| bool BreakpointLocation::IsEnabled() const { | |
| if (!m_owner.IsEnabled()) | |
| return false; | |
| if (m_options_up != nullptr) | |
| return m_options_up->IsEnabled(); | |
| return true; | |
| } | |
| llvm::Error BreakpointLocation::SetEnabled(bool enabled) { | |
| GetLocationOptions().SetEnabled(enabled); | |
| llvm::Error error = enabled ? ResolveBreakpointSite() : ClearBreakpointSite(); | |
| SendBreakpointLocationChangedEvent(enabled ? eBreakpointEventTypeEnabled | |
| : eBreakpointEventTypeDisabled); | |
| return error; | |
| } | |
| bool BreakpointLocation::IsAutoContinue() const { | |
| if (m_options_up && | |
| m_options_up->IsOptionSet(BreakpointOptions::eAutoContinue)) | |
| return m_options_up->IsAutoContinue(); | |
| return m_owner.IsAutoContinue(); | |
| } | |
| void BreakpointLocation::SetAutoContinue(bool auto_continue) { | |
| GetLocationOptions().SetAutoContinue(auto_continue); | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeAutoContinueChanged); | |
| } | |
| void BreakpointLocation::SetThreadID(lldb::tid_t thread_id) { | |
| SetThreadIDInternal(thread_id); | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); | |
| } | |
| lldb::tid_t BreakpointLocation::GetThreadID() { | |
| const ThreadSpec *thread_spec = | |
| GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) | |
| .GetThreadSpecNoCreate(); | |
| if (thread_spec) | |
| return thread_spec->GetTID(); | |
| return LLDB_INVALID_THREAD_ID; | |
| } | |
| void BreakpointLocation::SetThreadIndex(uint32_t index) { | |
| if (index != 0) | |
| GetLocationOptions().GetThreadSpec()->SetIndex(index); | |
| else { | |
| // If we're resetting this to an invalid thread id, then don't make an | |
| // options pointer just to do that. | |
| if (m_options_up != nullptr) | |
| m_options_up->GetThreadSpec()->SetIndex(index); | |
| } | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); | |
| } | |
| uint32_t BreakpointLocation::GetThreadIndex() const { | |
| const ThreadSpec *thread_spec = | |
| GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) | |
| .GetThreadSpecNoCreate(); | |
| if (thread_spec) | |
| return thread_spec->GetIndex(); | |
| return 0; | |
| } | |
| void BreakpointLocation::SetThreadName(const char *thread_name) { | |
| if (thread_name != nullptr) | |
| GetLocationOptions().GetThreadSpec()->SetName(thread_name); | |
| else { | |
| // If we're resetting this to an invalid thread id, then don't make an | |
| // options pointer just to do that. | |
| if (m_options_up != nullptr) | |
| m_options_up->GetThreadSpec()->SetName(thread_name); | |
| } | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); | |
| } | |
| const char *BreakpointLocation::GetThreadName() const { | |
| const ThreadSpec *thread_spec = | |
| GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) | |
| .GetThreadSpecNoCreate(); | |
| if (thread_spec) | |
| return thread_spec->GetName(); | |
| return nullptr; | |
| } | |
| void BreakpointLocation::SetQueueName(const char *queue_name) { | |
| if (queue_name != nullptr) | |
| GetLocationOptions().GetThreadSpec()->SetQueueName(queue_name); | |
| else { | |
| // If we're resetting this to an invalid thread id, then don't make an | |
| // options pointer just to do that. | |
| if (m_options_up != nullptr) | |
| m_options_up->GetThreadSpec()->SetQueueName(queue_name); | |
| } | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged); | |
| } | |
| const char *BreakpointLocation::GetQueueName() const { | |
| const ThreadSpec *thread_spec = | |
| GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) | |
| .GetThreadSpecNoCreate(); | |
| if (thread_spec) | |
| return thread_spec->GetQueueName(); | |
| return nullptr; | |
| } | |
| bool BreakpointLocation::InvokeCallback(StoppointCallbackContext *context) { | |
| if (m_options_up != nullptr && m_options_up->HasCallback()) | |
| return m_options_up->InvokeCallback(context, m_owner.GetID(), GetID()); | |
| return m_owner.InvokeCallback(context, GetID()); | |
| } | |
| bool BreakpointLocation::IsCallbackSynchronous() { | |
| if (m_options_up != nullptr && m_options_up->HasCallback()) | |
| return m_options_up->IsCallbackSynchronous(); | |
| return m_owner.GetOptions().IsCallbackSynchronous(); | |
| } | |
| void BreakpointLocation::SetCallback(BreakpointHitCallback callback, | |
| void *baton, bool is_synchronous) { | |
| // The default "Baton" class will keep a copy of "baton" and won't free or | |
| // delete it when it goes out of scope. | |
| GetLocationOptions().SetCallback( | |
| callback, std::make_shared<UntypedBaton>(baton), is_synchronous); | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged); | |
| } | |
| void BreakpointLocation::SetCallback(BreakpointHitCallback callback, | |
| const BatonSP &baton_sp, | |
| bool is_synchronous) { | |
| GetLocationOptions().SetCallback(callback, baton_sp, is_synchronous); | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged); | |
| } | |
| void BreakpointLocation::ClearCallback() { | |
| GetLocationOptions().ClearCallback(); | |
| } | |
| void BreakpointLocation::SetCondition(StopCondition condition) { | |
| GetLocationOptions().SetCondition(std::move(condition)); | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeConditionChanged); | |
| } | |
| const StopCondition &BreakpointLocation::GetCondition() const { | |
| return GetOptionsSpecifyingKind(BreakpointOptions::eCondition).GetCondition(); | |
| } | |
| bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx, | |
| Status &error) { | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| std::lock_guard<std::mutex> guard(m_condition_mutex); | |
| StopCondition condition = GetCondition(); | |
| if (!condition) { | |
| m_dil_expr_tree.reset(); | |
| m_user_expression_sp.reset(); | |
| return false; | |
| } | |
| // If the condition evaluation mode for this breakpoint is not specified, | |
| // use the value from the target setting. | |
| lldb::BreakpointConditionMode condition_mode = | |
| condition.GetMode() != lldb::eBreakpointConditionModeDefault | |
| ? condition.GetMode() | |
| : GetTarget().GetBreakpointsConditionMode(); | |
| // Attempt to parse the condition using Data Inspection Language (DIL). | |
| if (condition.GetHash() != m_condition_hash && exe_ctx.HasFrameScope() && | |
| condition_mode != lldb::eBreakpointConditionModeExpr) { | |
| // Lex the expression. | |
| auto lex_or_err = dil::DILLexer::Create(condition.GetText(), eDILModeFull); | |
| if (lex_or_err) { | |
| // Parse the expression. | |
| auto tree_or_error = dil::DILParser::Parse( | |
| exe_ctx, condition.GetText(), std::move(*lex_or_err), | |
| lldb::eNoDynamicValues, eDILModeFull); | |
| if (tree_or_error) { | |
| m_dil_expr_tree = std::move(*tree_or_error); | |
| m_condition_hash = condition.GetHash(); | |
| LLDB_LOG(log, "DIL successfully parsed the condition: {0}.", | |
| condition.GetText()); | |
| } else { | |
| m_dil_expr_tree.reset(); | |
| error = Status::FromError(tree_or_error.takeError()); | |
| LLDB_LOG(log, "Parsing condition with DIL failed:\n{0}.", error); | |
| } | |
| } else { | |
| m_dil_expr_tree.reset(); | |
| error = Status::FromError(lex_or_err.takeError()); | |
| LLDB_LOG(log, "Lexing condition with DIL failed:\n{0}.", error); | |
| } | |
| } | |
| // If the expression was parsed successfully, it can be evaluated separately | |
| // at every breakpoint location hit without having to parse it again. | |
| if (m_dil_expr_tree) { | |
| dil::Interpreter interpreter(exe_ctx, condition.GetText(), | |
| lldb::eNoDynamicValues, 0); | |
| // Evaluate the expression by DIL. | |
| auto valobj_or_error = interpreter.EvaluateTree(m_dil_expr_tree); | |
| if (valobj_or_error) { | |
| ValueObjectSP value_sp = *valobj_or_error; | |
| if (value_sp && value_sp->GetError().Success()) { | |
| llvm::Expected<bool> bool_result = value_sp->GetValueAsBool(); | |
| if (bool_result) { | |
| LLDB_LOG(log, | |
| "Condition successfully evaluated by DIL, result is {0}.\n", | |
| *bool_result ? "true" : "false"); | |
| return *bool_result; | |
| } | |
| error = Status::FromError(bool_result.takeError()); | |
| } | |
| } else { | |
| error = Status::FromError(valobj_or_error.takeError()); | |
| } | |
| // Evaluation failed. | |
| LLDB_LOG(log, "Evaluating condition with DIL failed:\n{0}.", error); | |
| m_dil_expr_tree.reset(); | |
| m_condition_hash = 0; | |
| } | |
| // DIL evaluation failed, trigger the breakpoint and return the error. | |
| if (condition_mode == lldb::eBreakpointConditionModeDIL) | |
| return true; | |
| // The condition evaluation proceeds to UserExpression here. | |
| error.Clear(); | |
| DiagnosticManager diagnostics; | |
| if (condition.GetHash() != m_condition_hash || !m_user_expression_sp || | |
| !m_user_expression_sp->IsParseCacheable() || | |
| !m_user_expression_sp->MatchesContext(exe_ctx)) { | |
| LanguageType language = condition.GetLanguage(); | |
| if (language == lldb::eLanguageTypeUnknown) { | |
| // See if we can figure out the language from the frame, otherwise use the | |
| // default language: | |
| if (CompileUnit *comp_unit = | |
| m_address.CalculateSymbolContextCompileUnit()) | |
| language = comp_unit->GetLanguage(); | |
| } | |
| m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage( | |
| condition.GetText(), llvm::StringRef(), SourceLanguage{language}, | |
| Expression::eResultTypeAny, EvaluateExpressionOptions(), nullptr, | |
| error)); | |
| if (error.Fail()) { | |
| LLDB_LOGF(log, "Error getting condition expression: %s.", | |
| error.AsCString()); | |
| m_user_expression_sp.reset(); | |
| return true; | |
| } | |
| if (!m_user_expression_sp->Parse(diagnostics, exe_ctx, | |
| eExecutionPolicyOnlyWhenNeeded, true, | |
| false)) { | |
| error = Status::FromError( | |
| diagnostics.GetAsError(lldb::eExpressionParseError, | |
| "Couldn't parse conditional expression:")); | |
| m_user_expression_sp.reset(); | |
| return true; | |
| } | |
| m_condition_hash = condition.GetHash(); | |
| } | |
| // We need to make sure the user sees any parse errors in their condition, so | |
| // we'll hook the constructor errors up to the debugger's Async I/O. | |
| ValueObjectSP result_value_sp; | |
| EvaluateExpressionOptions options; | |
| options.SetUnwindOnError(true); | |
| options.SetIgnoreBreakpoints(true); | |
| options.SetTryAllThreads(true); | |
| options.SetSuppressPersistentResult( | |
| true); // Don't generate a user variable for condition expressions. | |
| Status expr_error; | |
| diagnostics.Clear(); | |
| ExpressionVariableSP result_variable_sp; | |
| ExpressionResults result_code = m_user_expression_sp->Execute( | |
| diagnostics, exe_ctx, options, m_user_expression_sp, result_variable_sp); | |
| bool ret; | |
| if (result_code == eExpressionCompleted) { | |
| if (!result_variable_sp) { | |
| error = Status::FromErrorString("Expression did not return a result"); | |
| return false; | |
| } | |
| result_value_sp = result_variable_sp->GetValueObject(); | |
| if (result_value_sp) { | |
| ret = result_value_sp->IsLogicalTrue(error); | |
| if (log) { | |
| if (error.Success()) { | |
| LLDB_LOGF(log, | |
| "Condition successfully evaluated by UserExpression, " | |
| "result is %s.\n", | |
| ret ? "true" : "false"); | |
| } else { | |
| error = Status::FromErrorString( | |
| "Failed to get an integer result from the expression"); | |
| ret = false; | |
| } | |
| } | |
| } else { | |
| ret = false; | |
| error = Status::FromErrorString( | |
| "Failed to get any result from the expression"); | |
| } | |
| } else { | |
| ret = false; | |
| error = Status::FromError(diagnostics.GetAsError( | |
| lldb::eExpressionParseError, "Couldn't execute expression:")); | |
| } | |
| return ret; | |
| } | |
| uint32_t BreakpointLocation::GetIgnoreCount() const { | |
| return GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount) | |
| .GetIgnoreCount(); | |
| } | |
| void BreakpointLocation::SetIgnoreCount(uint32_t n) { | |
| GetLocationOptions().SetIgnoreCount(n); | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeIgnoreChanged); | |
| } | |
| void BreakpointLocation::DecrementIgnoreCount() { | |
| if (m_options_up != nullptr) { | |
| uint32_t loc_ignore = m_options_up->GetIgnoreCount(); | |
| if (loc_ignore != 0) | |
| m_options_up->SetIgnoreCount(loc_ignore - 1); | |
| } | |
| } | |
| bool BreakpointLocation::IgnoreCountShouldStop() { | |
| uint32_t owner_ignore = GetBreakpoint().GetIgnoreCount(); | |
| uint32_t loc_ignore = 0; | |
| if (m_options_up != nullptr) | |
| loc_ignore = m_options_up->GetIgnoreCount(); | |
| if (loc_ignore != 0 || owner_ignore != 0) { | |
| m_owner.DecrementIgnoreCount(); | |
| DecrementIgnoreCount(); // Have to decrement our owners' ignore count, | |
| // since it won't get a chance to. | |
| return false; | |
| } | |
| return true; | |
| } | |
| BreakpointOptions &BreakpointLocation::GetLocationOptions() { | |
| // If we make the copy we don't copy the callbacks because that is | |
| // potentially expensive and we don't want to do that for the simple case | |
| // where someone is just disabling the location. | |
| if (m_options_up == nullptr) | |
| m_options_up = std::make_unique<BreakpointOptions>(false); | |
| return *m_options_up; | |
| } | |
| bool BreakpointLocation::ValidForThisThread(Thread &thread) { | |
| return thread.MatchesSpec( | |
| GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) | |
| .GetThreadSpecNoCreate()); | |
| } | |
| BreakpointLocationSP | |
| BreakpointLocation::WasHit(StoppointCallbackContext *context) { | |
| // Only the BreakpointResolverScripted provides WasHit. | |
| BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver(); | |
| BreakpointResolverScripted *scripted = | |
| llvm::dyn_cast<BreakpointResolverScripted>(resolver_sp.get()); | |
| if (!scripted) | |
| return shared_from_this(); | |
| StackFrameSP frame_sp = context->exe_ctx_ref.GetFrameSP(); | |
| if (!frame_sp) | |
| return shared_from_this(); | |
| BreakpointLocationSP return_loc_sp = | |
| scripted->WasHit(frame_sp, shared_from_this()); | |
| // If this is a facade location, then we won't have bumped its hit count | |
| // while processing the original location hit. Do so here. We don't need | |
| // to bump the breakpoint's hit count, however, since hitting the real | |
| // location would have already done that. | |
| // Also we have to check the enabled state here, since we would never have | |
| // gotten here with a real location... | |
| if (return_loc_sp && return_loc_sp->IsFacade()) { | |
| if (return_loc_sp->IsEnabled()) | |
| return_loc_sp->m_hit_counter.Increment(); | |
| else | |
| return {}; | |
| } | |
| return return_loc_sp; | |
| } | |
| // RETURNS - true if we should stop at this breakpoint, false if we | |
| // should continue. Note, we don't check the thread spec for the breakpoint | |
| // here, since if the breakpoint is not for this thread, then the event won't | |
| // even get reported, so the check is redundant. | |
| bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context, | |
| lldb::BreakpointLocationSP &facade_loc_sp) { | |
| bool should_stop = true; | |
| Log *log = GetLog(LLDBLog::Breakpoints); | |
| // Do this first, if a location is disabled, it shouldn't increment its hit | |
| // count. | |
| if (!IsEnabled()) | |
| return false; | |
| // Next check WasHit: | |
| BreakpointLocationSP loc_hit_sp = WasHit(context); | |
| if (!loc_hit_sp) { | |
| // We bump the hit counts in StopInfoBreakpoint::ShouldStopSynchronous, | |
| // before we call into each location's ShouldStop. So we need to undo | |
| // that here. | |
| UndoBumpHitCount(); | |
| return false; | |
| } | |
| // If the location hit was not us, it was a facade location, in which case | |
| // we should use the facade location's callbacks, etc. Those will all be | |
| // run in the asynchronous phase, so for now we just have to record the fact | |
| // that we should treat this as a facade hit. This is strictly an out | |
| // parameter, so clear it if this isn't a facade hit. | |
| if (loc_hit_sp.get() != this) | |
| facade_loc_sp = loc_hit_sp; | |
| else | |
| facade_loc_sp.reset(); | |
| // We only run synchronous callbacks in ShouldStop: | |
| context->is_synchronous = true; | |
| should_stop = InvokeCallback(context); | |
| if (log) { | |
| StreamString s; | |
| GetDescription(&s, lldb::eDescriptionLevelVerbose); | |
| LLDB_LOGF(log, "Hit breakpoint location: %s, %s.\n", s.GetData(), | |
| should_stop ? "stopping" : "continuing"); | |
| if (facade_loc_sp) { | |
| s.Clear(); | |
| facade_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); | |
| LLDB_LOGF(log, "Attributing to facade location: %s.\n", s.GetData()); | |
| } | |
| } | |
| return should_stop; | |
| } | |
| void BreakpointLocation::BumpHitCount() { | |
| if (IsEnabled()) { | |
| // Step our hit count, and also step the hit count of the owner. | |
| m_hit_counter.Increment(); | |
| m_owner.m_hit_counter.Increment(); | |
| } | |
| } | |
| void BreakpointLocation::UndoBumpHitCount() { | |
| if (IsEnabled()) { | |
| // Step our hit count, and also step the hit count of the owner. | |
| m_hit_counter.Decrement(); | |
| m_owner.m_hit_counter.Decrement(); | |
| } | |
| } | |
| bool BreakpointLocation::IsResolved() const { | |
| bool has_site = m_bp_site_sp.get() != nullptr; | |
| // Facade locations are currently always considered resolved. | |
| return has_site || IsFacade(); | |
| } | |
| lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const { | |
| return m_bp_site_sp; | |
| } | |
| llvm::Error BreakpointLocation::ResolveBreakpointSite() { | |
| // This might be a facade location, which doesn't have an address. | |
| // In that case, don't attempt to make a site. | |
| if (m_bp_site_sp || IsFacade()) | |
| return llvm::Error::success(); | |
| Process *process = m_owner.GetTarget().GetProcessSP().get(); | |
| if (process == nullptr) | |
| return llvm::createStringError("no process"); | |
| lldb::break_id_t new_id = | |
| process->CreateBreakpointSite(shared_from_this(), m_owner.IsHardware()); | |
| if (new_id == LLDB_INVALID_BREAK_ID) | |
| return llvm::createStringError( | |
| llvm::formatv("Failed to add breakpoint site at {0:x}", | |
| m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()))); | |
| if (!IsResolved()) | |
| return llvm::createStringError( | |
| "breakpoint site created but location is still unresolved"); | |
| return llvm::Error::success(); | |
| } | |
| bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) { | |
| m_bp_site_sp = bp_site_sp; | |
| SendBreakpointLocationChangedEvent(eBreakpointEventTypeLocationsResolved); | |
| return true; | |
| } | |
| llvm::Error BreakpointLocation::ClearBreakpointSite() { | |
| if (!m_bp_site_sp) { | |
| // This might be a Facade Location, which don't have sites or addresses | |
| if (IsFacade()) | |
| return llvm::Error::success(); | |
| return llvm::createStringError("no breakpoint site to clear"); | |
| } | |
| // If the process exists, get it to remove the owner, it will remove the | |
| // physical implementation of the breakpoint as well if there are no more | |
| // owners. Otherwise just remove this owner. | |
| if (ProcessSP process_sp = m_owner.GetTarget().GetProcessSP()) | |
| process_sp->RemoveConstituentFromBreakpointSite(GetBreakpoint().GetID(), | |
| GetID(), m_bp_site_sp); | |
| else | |
| m_bp_site_sp->RemoveConstituent(GetBreakpoint().GetID(), GetID()); | |
| m_bp_site_sp.reset(); | |
| return llvm::Error::success(); | |
| } | |
| void BreakpointLocation::GetDescription(Stream *s, | |
| lldb::DescriptionLevel level) { | |
| SymbolContext sc; | |
| // If this is a scripted breakpoint, give it a chance to describe its | |
| // locations: | |
| std::optional<std::string> scripted_opt; | |
| BreakpointResolverSP resolver_sp = GetBreakpoint().GetResolver(); | |
| BreakpointResolverScripted *scripted = | |
| llvm::dyn_cast<BreakpointResolverScripted>(resolver_sp.get()); | |
| if (scripted) | |
| scripted_opt = scripted->GetLocationDescription(shared_from_this(), level); | |
| bool is_scripted_desc = scripted_opt.has_value(); | |
| // If the description level is "initial" then the breakpoint is printing out | |
| // our initial state, and we should let it decide how it wants to print our | |
| // label. | |
| if (level != eDescriptionLevelInitial) { | |
| s->Indent(); | |
| BreakpointID::GetCanonicalReference(s, m_owner.GetID(), GetID()); | |
| } | |
| if (level == lldb::eDescriptionLevelBrief) | |
| return; | |
| if (level != eDescriptionLevelInitial) | |
| s->PutCString(": "); | |
| if (level == lldb::eDescriptionLevelVerbose) | |
| s->IndentMore(); | |
| if (is_scripted_desc) { | |
| s->PutCString(scripted_opt->c_str()); | |
| } else if (m_address.IsSectionOffset()) { | |
| m_address.CalculateSymbolContext(&sc); | |
| if (level == lldb::eDescriptionLevelFull || | |
| level == eDescriptionLevelInitial) { | |
| if (IsReExported()) | |
| s->PutCString("re-exported target = "); | |
| else | |
| s->PutCString("where = "); | |
| // If there's a preferred line entry for printing, use that. | |
| bool show_function_info = true; | |
| if (auto preferred = GetPreferredLineEntry()) { | |
| sc.line_entry = *preferred; | |
| // FIXME: We're going to get the function name wrong when the preferred | |
| // line entry is not the lowest one. For now, just leave the function | |
| // out in this case, but we really should also figure out how to easily | |
| // fake the function name here. | |
| show_function_info = false; | |
| } | |
| sc.DumpStopContext(s, m_owner.GetTarget().GetProcessSP().get(), m_address, | |
| false, true, false, show_function_info, | |
| show_function_info, show_function_info); | |
| } else { | |
| if (sc.module_sp) { | |
| s->EOL(); | |
| s->Indent("module = "); | |
| sc.module_sp->GetFileSpec().Dump(s->AsRawOstream()); | |
| } | |
| if (sc.comp_unit != nullptr) { | |
| s->EOL(); | |
| s->Indent("compile unit = "); | |
| s->PutCString(sc.comp_unit->GetPrimaryFile().GetFilename()); | |
| if (sc.function != nullptr) { | |
| s->EOL(); | |
| s->Indent("function = "); | |
| s->PutCString(sc.function->GetName().AsCString("<unknown>")); | |
| if (ConstString mangled_name = | |
| sc.function->GetMangled().GetMangledName()) { | |
| s->EOL(); | |
| s->Indent("mangled function = "); | |
| s->PutCString(mangled_name); | |
| } | |
| } | |
| if (sc.line_entry.line > 0) { | |
| s->EOL(); | |
| s->Indent("location = "); | |
| if (auto preferred = GetPreferredLineEntry()) | |
| preferred->DumpStopContext(s, true); | |
| else | |
| sc.line_entry.DumpStopContext(s, true); | |
| } | |
| } else { | |
| // If we don't have a comp unit, see if we have a symbol we can print. | |
| if (sc.symbol) { | |
| s->EOL(); | |
| if (IsReExported()) | |
| s->Indent("re-exported target = "); | |
| else | |
| s->Indent("symbol = "); | |
| s->PutCString(sc.symbol->GetName().AsCString("<unknown>")); | |
| } | |
| } | |
| } | |
| } | |
| if (level == lldb::eDescriptionLevelVerbose) { | |
| s->EOL(); | |
| s->Indent(); | |
| } | |
| if (!is_scripted_desc) { | |
| if (m_address.IsSectionOffset() && | |
| (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)) | |
| s->PutCString(", "); | |
| s->PutCString("address = "); | |
| ExecutionContextScope *exe_scope = nullptr; | |
| Target *target = &m_owner.GetTarget(); | |
| if (target) | |
| exe_scope = target->GetProcessSP().get(); | |
| if (exe_scope == nullptr) | |
| exe_scope = target; | |
| if (level == eDescriptionLevelInitial) | |
| m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, | |
| Address::DumpStyleFileAddress); | |
| else | |
| m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, | |
| Address::DumpStyleModuleWithFileAddress); | |
| if (IsIndirect() && m_bp_site_sp) { | |
| Address resolved_address; | |
| resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target); | |
| const Symbol *resolved_symbol = | |
| resolved_address.CalculateSymbolContextSymbol(); | |
| if (resolved_symbol) { | |
| if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial) | |
| s->PutCString(", "); | |
| else if (level == lldb::eDescriptionLevelVerbose) { | |
| s->EOL(); | |
| s->Indent(); | |
| } | |
| s->Printf("indirect target = %s", | |
| resolved_symbol->GetName().GetCString()); | |
| } | |
| } | |
| } | |
| // FIXME: scripted breakpoint are currently always resolved. Does this seem | |
| // right? If they don't add any scripted locations, we shouldn't consider them | |
| // resolved. | |
| bool is_resolved = is_scripted_desc || IsResolved(); | |
| // A scripted breakpoint might be resolved but not have a site. Be sure to | |
| // check for that. | |
| bool is_hardware = !is_scripted_desc && IsResolved() && m_bp_site_sp && | |
| m_bp_site_sp->IsHardware(); | |
| if (level == lldb::eDescriptionLevelVerbose) { | |
| s->EOL(); | |
| s->Indent(); | |
| s->Printf("resolved = %s\n", is_resolved ? "true" : "false"); | |
| s->Indent(); | |
| s->Printf("hardware = %s\n", is_hardware ? "true" : "false"); | |
| s->Indent(); | |
| s->Printf("hit count = %-4u\n", GetHitCount()); | |
| if (m_options_up) { | |
| s->Indent(); | |
| m_options_up->GetDescription(s, level); | |
| s->EOL(); | |
| } | |
| s->IndentLess(); | |
| } else if (level != eDescriptionLevelInitial) { | |
| s->Printf(", %sresolved, %shit count = %u ", (is_resolved ? "" : "un"), | |
| (is_hardware ? "hardware, " : ""), GetHitCount()); | |
| if (m_options_up) { | |
| m_options_up->GetDescription(s, level); | |
| } | |
| } | |
| } | |
| void BreakpointLocation::Dump(Stream *s) const { | |
| if (s == nullptr) | |
| return; | |
| bool is_resolved = IsResolved(); | |
| bool is_hardware = is_resolved && m_bp_site_sp->IsHardware(); | |
| lldb::tid_t tid = GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec) | |
| .GetThreadSpecNoCreate() | |
| ->GetTID(); | |
| s->Printf("BreakpointLocation %u: tid = %4.4" PRIx64 | |
| " load addr = 0x%8.8" PRIx64 " state = %s type = %s breakpoint " | |
| "hit_count = %-4u ignore_count = %-4u", | |
| GetID(), tid, | |
| (uint64_t)m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()), | |
| (m_options_up ? m_options_up->IsEnabled() : m_owner.IsEnabled()) | |
| ? "enabled " | |
| : "disabled", | |
| is_hardware ? "hardware" : "software", GetHitCount(), | |
| GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount) | |
| .GetIgnoreCount()); | |
| } | |
| void BreakpointLocation::SendBreakpointLocationChangedEvent( | |
| lldb::BreakpointEventType eventKind) { | |
| if (!m_owner.IsInternal()) { | |
| auto data_sp = std::make_shared<Breakpoint::BreakpointEventData>( | |
| eventKind, m_owner.shared_from_this()); | |
| data_sp->GetBreakpointLocationCollection().Add(shared_from_this()); | |
| m_owner.GetTarget().NotifyBreakpointChanged(m_owner, data_sp); | |
| } | |
| } | |
| std::optional<uint32_t> BreakpointLocation::GetSuggestedStackFrameIndex() { | |
| auto preferred_opt = GetPreferredLineEntry(); | |
| if (!preferred_opt) | |
| return {}; | |
| LineEntry preferred = *preferred_opt; | |
| SymbolContext sc; | |
| if (!m_address.CalculateSymbolContext(&sc)) | |
| return {}; | |
| // Don't return anything special if frame 0 is the preferred line entry. | |
| // We not really telling the stack frame list to do anything special in that | |
| // case. | |
| if (!LineEntry::Compare(sc.line_entry, preferred)) | |
| return {}; | |
| if (!sc.block) | |
| return {}; | |
| // Blocks have their line info in Declaration form, so make one here: | |
| Declaration preferred_decl(preferred.GetFile(), preferred.line, | |
| preferred.column); | |
| uint32_t depth = 0; | |
| Block *inlined_block = sc.block->GetContainingInlinedBlock(); | |
| while (inlined_block) { | |
| // If we've moved to a block that this isn't the start of, that's not | |
| // our inlining info or call site, so we can stop here. | |
| Address start_address; | |
| if (!inlined_block->GetStartAddress(start_address) || | |
| start_address != m_address) | |
| return {}; | |
| const InlineFunctionInfo *info = inlined_block->GetInlinedFunctionInfo(); | |
| if (info) { | |
| if (preferred_decl == info->GetDeclaration()) | |
| return depth; | |
| if (preferred_decl == info->GetCallSite()) | |
| return depth + 1; | |
| } | |
| inlined_block = inlined_block->GetInlinedParent(); | |
| depth++; | |
| } | |
| return {}; | |
| } | |
| void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) { | |
| m_address = swap_from->m_address; | |
| m_should_resolve_indirect_functions = | |
| swap_from->m_should_resolve_indirect_functions; | |
| m_is_reexported = swap_from->m_is_reexported; | |
| m_is_indirect = swap_from->m_is_indirect; | |
| m_dil_expr_tree.reset(); | |
| m_user_expression_sp.reset(); | |
| m_condition_hash = 0; | |
| } | |
| void BreakpointLocation::SetThreadIDInternal(lldb::tid_t thread_id) { | |
| if (thread_id != LLDB_INVALID_THREAD_ID) { | |
| GetLocationOptions().SetThreadID(thread_id); | |
| } else { | |
| // If we're resetting this to an invalid thread id, then don't make an | |
| // options pointer just to do that. | |
| if (m_options_up != nullptr) | |
| m_options_up->SetThreadID(thread_id); | |
| } | |
| } |