Path: blob/main/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp
39587 views
//===-- BreakpointLocation.cpp --------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Breakpoint/BreakpointLocation.h"9#include "lldb/Breakpoint/BreakpointID.h"10#include "lldb/Breakpoint/StoppointCallbackContext.h"11#include "lldb/Core/Debugger.h"12#include "lldb/Core/Module.h"13#include "lldb/Core/ValueObject.h"14#include "lldb/Expression/DiagnosticManager.h"15#include "lldb/Expression/ExpressionVariable.h"16#include "lldb/Expression/UserExpression.h"17#include "lldb/Symbol/CompileUnit.h"18#include "lldb/Symbol/Symbol.h"19#include "lldb/Symbol/TypeSystem.h"20#include "lldb/Target/Process.h"21#include "lldb/Target/Target.h"22#include "lldb/Target/Thread.h"23#include "lldb/Target/ThreadSpec.h"24#include "lldb/Utility/LLDBLog.h"25#include "lldb/Utility/Log.h"26#include "lldb/Utility/StreamString.h"2728using namespace lldb;29using namespace lldb_private;3031BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner,32const Address &addr, lldb::tid_t tid,33bool hardware, bool check_for_resolver)34: m_should_resolve_indirect_functions(false), m_is_reexported(false),35m_is_indirect(false), m_address(addr), m_owner(owner),36m_condition_hash(0), m_loc_id(loc_id), m_hit_counter() {37if (check_for_resolver) {38Symbol *symbol = m_address.CalculateSymbolContextSymbol();39if (symbol && symbol->IsIndirect()) {40SetShouldResolveIndirectFunctions(true);41}42}4344SetThreadIDInternal(tid);45}4647BreakpointLocation::~BreakpointLocation() { ClearBreakpointSite(); }4849lldb::addr_t BreakpointLocation::GetLoadAddress() const {50return m_address.GetOpcodeLoadAddress(&m_owner.GetTarget());51}5253const BreakpointOptions &BreakpointLocation::GetOptionsSpecifyingKind(54BreakpointOptions::OptionKind kind) const {55if (m_options_up && m_options_up->IsOptionSet(kind))56return *m_options_up;57else58return m_owner.GetOptions();59}6061Address &BreakpointLocation::GetAddress() { return m_address; }6263Breakpoint &BreakpointLocation::GetBreakpoint() { return m_owner; }6465Target &BreakpointLocation::GetTarget() { return m_owner.GetTarget(); }6667bool BreakpointLocation::IsEnabled() const {68if (!m_owner.IsEnabled())69return false;70else if (m_options_up != nullptr)71return m_options_up->IsEnabled();72else73return true;74}7576void BreakpointLocation::SetEnabled(bool enabled) {77GetLocationOptions().SetEnabled(enabled);78if (enabled) {79ResolveBreakpointSite();80} else {81ClearBreakpointSite();82}83SendBreakpointLocationChangedEvent(enabled ? eBreakpointEventTypeEnabled84: eBreakpointEventTypeDisabled);85}8687bool BreakpointLocation::IsAutoContinue() const {88if (m_options_up &&89m_options_up->IsOptionSet(BreakpointOptions::eAutoContinue))90return m_options_up->IsAutoContinue();91else92return m_owner.IsAutoContinue();93}9495void BreakpointLocation::SetAutoContinue(bool auto_continue) {96GetLocationOptions().SetAutoContinue(auto_continue);97SendBreakpointLocationChangedEvent(eBreakpointEventTypeAutoContinueChanged);98}99100void BreakpointLocation::SetThreadID(lldb::tid_t thread_id) {101SetThreadIDInternal(thread_id);102SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);103}104105lldb::tid_t BreakpointLocation::GetThreadID() {106const ThreadSpec *thread_spec =107GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)108.GetThreadSpecNoCreate();109if (thread_spec)110return thread_spec->GetTID();111else112return LLDB_INVALID_THREAD_ID;113}114115void BreakpointLocation::SetThreadIndex(uint32_t index) {116if (index != 0)117GetLocationOptions().GetThreadSpec()->SetIndex(index);118else {119// If we're resetting this to an invalid thread id, then don't make an120// options pointer just to do that.121if (m_options_up != nullptr)122m_options_up->GetThreadSpec()->SetIndex(index);123}124SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);125}126127uint32_t BreakpointLocation::GetThreadIndex() const {128const ThreadSpec *thread_spec =129GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)130.GetThreadSpecNoCreate();131if (thread_spec)132return thread_spec->GetIndex();133else134return 0;135}136137void BreakpointLocation::SetThreadName(const char *thread_name) {138if (thread_name != nullptr)139GetLocationOptions().GetThreadSpec()->SetName(thread_name);140else {141// If we're resetting this to an invalid thread id, then don't make an142// options pointer just to do that.143if (m_options_up != nullptr)144m_options_up->GetThreadSpec()->SetName(thread_name);145}146SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);147}148149const char *BreakpointLocation::GetThreadName() const {150const ThreadSpec *thread_spec =151GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)152.GetThreadSpecNoCreate();153if (thread_spec)154return thread_spec->GetName();155else156return nullptr;157}158159void BreakpointLocation::SetQueueName(const char *queue_name) {160if (queue_name != nullptr)161GetLocationOptions().GetThreadSpec()->SetQueueName(queue_name);162else {163// If we're resetting this to an invalid thread id, then don't make an164// options pointer just to do that.165if (m_options_up != nullptr)166m_options_up->GetThreadSpec()->SetQueueName(queue_name);167}168SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);169}170171const char *BreakpointLocation::GetQueueName() const {172const ThreadSpec *thread_spec =173GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)174.GetThreadSpecNoCreate();175if (thread_spec)176return thread_spec->GetQueueName();177else178return nullptr;179}180181bool BreakpointLocation::InvokeCallback(StoppointCallbackContext *context) {182if (m_options_up != nullptr && m_options_up->HasCallback())183return m_options_up->InvokeCallback(context, m_owner.GetID(), GetID());184else185return m_owner.InvokeCallback(context, GetID());186}187188bool BreakpointLocation::IsCallbackSynchronous() {189if (m_options_up != nullptr && m_options_up->HasCallback())190return m_options_up->IsCallbackSynchronous();191else192return m_owner.GetOptions().IsCallbackSynchronous();193}194195void BreakpointLocation::SetCallback(BreakpointHitCallback callback,196void *baton, bool is_synchronous) {197// The default "Baton" class will keep a copy of "baton" and won't free or198// delete it when it goes out of scope.199GetLocationOptions().SetCallback(200callback, std::make_shared<UntypedBaton>(baton), is_synchronous);201SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged);202}203204void BreakpointLocation::SetCallback(BreakpointHitCallback callback,205const BatonSP &baton_sp,206bool is_synchronous) {207GetLocationOptions().SetCallback(callback, baton_sp, is_synchronous);208SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged);209}210211void BreakpointLocation::ClearCallback() {212GetLocationOptions().ClearCallback();213}214215void BreakpointLocation::SetCondition(const char *condition) {216GetLocationOptions().SetCondition(condition);217SendBreakpointLocationChangedEvent(eBreakpointEventTypeConditionChanged);218}219220const char *BreakpointLocation::GetConditionText(size_t *hash) const {221return GetOptionsSpecifyingKind(BreakpointOptions::eCondition)222.GetConditionText(hash);223}224225bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,226Status &error) {227Log *log = GetLog(LLDBLog::Breakpoints);228229std::lock_guard<std::mutex> guard(m_condition_mutex);230231size_t condition_hash;232const char *condition_text = GetConditionText(&condition_hash);233234if (!condition_text) {235m_user_expression_sp.reset();236return false;237}238239error.Clear();240241DiagnosticManager diagnostics;242243if (condition_hash != m_condition_hash || !m_user_expression_sp ||244!m_user_expression_sp->IsParseCacheable() ||245!m_user_expression_sp->MatchesContext(exe_ctx)) {246LanguageType language = eLanguageTypeUnknown;247// See if we can figure out the language from the frame, otherwise use the248// default language:249CompileUnit *comp_unit = m_address.CalculateSymbolContextCompileUnit();250if (comp_unit)251language = comp_unit->GetLanguage();252253m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage(254condition_text, llvm::StringRef(), language, Expression::eResultTypeAny,255EvaluateExpressionOptions(), nullptr, error));256if (error.Fail()) {257LLDB_LOGF(log, "Error getting condition expression: %s.",258error.AsCString());259m_user_expression_sp.reset();260return true;261}262263if (!m_user_expression_sp->Parse(diagnostics, exe_ctx,264eExecutionPolicyOnlyWhenNeeded, true,265false)) {266error.SetErrorStringWithFormat(267"Couldn't parse conditional expression:\n%s",268diagnostics.GetString().c_str());269m_user_expression_sp.reset();270return true;271}272273m_condition_hash = condition_hash;274}275276// We need to make sure the user sees any parse errors in their condition, so277// we'll hook the constructor errors up to the debugger's Async I/O.278279ValueObjectSP result_value_sp;280281EvaluateExpressionOptions options;282options.SetUnwindOnError(true);283options.SetIgnoreBreakpoints(true);284options.SetTryAllThreads(true);285options.SetSuppressPersistentResult(286true); // Don't generate a user variable for condition expressions.287288Status expr_error;289290diagnostics.Clear();291292ExpressionVariableSP result_variable_sp;293294ExpressionResults result_code = m_user_expression_sp->Execute(295diagnostics, exe_ctx, options, m_user_expression_sp, result_variable_sp);296297bool ret;298299if (result_code == eExpressionCompleted) {300if (!result_variable_sp) {301error.SetErrorString("Expression did not return a result");302return false;303}304305result_value_sp = result_variable_sp->GetValueObject();306307if (result_value_sp) {308ret = result_value_sp->IsLogicalTrue(error);309if (log) {310if (error.Success()) {311LLDB_LOGF(log, "Condition successfully evaluated, result is %s.\n",312ret ? "true" : "false");313} else {314error.SetErrorString(315"Failed to get an integer result from the expression");316ret = false;317}318}319} else {320ret = false;321error.SetErrorString("Failed to get any result from the expression");322}323} else {324ret = false;325error.SetErrorStringWithFormat("Couldn't execute expression:\n%s",326diagnostics.GetString().c_str());327}328329return ret;330}331332uint32_t BreakpointLocation::GetIgnoreCount() const {333return GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount)334.GetIgnoreCount();335}336337void BreakpointLocation::SetIgnoreCount(uint32_t n) {338GetLocationOptions().SetIgnoreCount(n);339SendBreakpointLocationChangedEvent(eBreakpointEventTypeIgnoreChanged);340}341342void BreakpointLocation::DecrementIgnoreCount() {343if (m_options_up != nullptr) {344uint32_t loc_ignore = m_options_up->GetIgnoreCount();345if (loc_ignore != 0)346m_options_up->SetIgnoreCount(loc_ignore - 1);347}348}349350bool BreakpointLocation::IgnoreCountShouldStop() {351uint32_t owner_ignore = GetBreakpoint().GetIgnoreCount();352uint32_t loc_ignore = 0;353if (m_options_up != nullptr)354loc_ignore = m_options_up->GetIgnoreCount();355356if (loc_ignore != 0 || owner_ignore != 0) {357m_owner.DecrementIgnoreCount();358DecrementIgnoreCount(); // Have to decrement our owners' ignore count,359// since it won't get a chance to.360return false;361}362return true;363}364365BreakpointOptions &BreakpointLocation::GetLocationOptions() {366// If we make the copy we don't copy the callbacks because that is367// potentially expensive and we don't want to do that for the simple case368// where someone is just disabling the location.369if (m_options_up == nullptr)370m_options_up = std::make_unique<BreakpointOptions>(false);371372return *m_options_up;373}374375bool BreakpointLocation::ValidForThisThread(Thread &thread) {376return thread.MatchesSpec(377GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)378.GetThreadSpecNoCreate());379}380381// RETURNS - true if we should stop at this breakpoint, false if we382// should continue. Note, we don't check the thread spec for the breakpoint383// here, since if the breakpoint is not for this thread, then the event won't384// even get reported, so the check is redundant.385386bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {387bool should_stop = true;388Log *log = GetLog(LLDBLog::Breakpoints);389390// Do this first, if a location is disabled, it shouldn't increment its hit391// count.392if (!IsEnabled())393return false;394395// We only run synchronous callbacks in ShouldStop:396context->is_synchronous = true;397should_stop = InvokeCallback(context);398399if (log) {400StreamString s;401GetDescription(&s, lldb::eDescriptionLevelVerbose);402LLDB_LOGF(log, "Hit breakpoint location: %s, %s.\n", s.GetData(),403should_stop ? "stopping" : "continuing");404}405406return should_stop;407}408409void BreakpointLocation::BumpHitCount() {410if (IsEnabled()) {411// Step our hit count, and also step the hit count of the owner.412m_hit_counter.Increment();413m_owner.m_hit_counter.Increment();414}415}416417void BreakpointLocation::UndoBumpHitCount() {418if (IsEnabled()) {419// Step our hit count, and also step the hit count of the owner.420m_hit_counter.Decrement();421m_owner.m_hit_counter.Decrement();422}423}424425bool BreakpointLocation::IsResolved() const {426return m_bp_site_sp.get() != nullptr;427}428429lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const {430return m_bp_site_sp;431}432433bool BreakpointLocation::ResolveBreakpointSite() {434if (m_bp_site_sp)435return true;436437Process *process = m_owner.GetTarget().GetProcessSP().get();438if (process == nullptr)439return false;440441lldb::break_id_t new_id =442process->CreateBreakpointSite(shared_from_this(), m_owner.IsHardware());443444if (new_id == LLDB_INVALID_BREAK_ID) {445Log *log = GetLog(LLDBLog::Breakpoints);446if (log)447log->Warning("Failed to add breakpoint site at 0x%" PRIx64,448m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()));449}450451return IsResolved();452}453454bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) {455m_bp_site_sp = bp_site_sp;456SendBreakpointLocationChangedEvent(eBreakpointEventTypeLocationsResolved);457return true;458}459460bool BreakpointLocation::ClearBreakpointSite() {461if (m_bp_site_sp.get()) {462ProcessSP process_sp(m_owner.GetTarget().GetProcessSP());463// If the process exists, get it to remove the owner, it will remove the464// physical implementation of the breakpoint as well if there are no more465// owners. Otherwise just remove this owner.466if (process_sp)467process_sp->RemoveConstituentFromBreakpointSite(GetBreakpoint().GetID(),468GetID(), m_bp_site_sp);469else470m_bp_site_sp->RemoveConstituent(GetBreakpoint().GetID(), GetID());471472m_bp_site_sp.reset();473return true;474}475return false;476}477478void BreakpointLocation::GetDescription(Stream *s,479lldb::DescriptionLevel level) {480SymbolContext sc;481482// If the description level is "initial" then the breakpoint is printing out483// our initial state, and we should let it decide how it wants to print our484// label.485if (level != eDescriptionLevelInitial) {486s->Indent();487BreakpointID::GetCanonicalReference(s, m_owner.GetID(), GetID());488}489490if (level == lldb::eDescriptionLevelBrief)491return;492493if (level != eDescriptionLevelInitial)494s->PutCString(": ");495496if (level == lldb::eDescriptionLevelVerbose)497s->IndentMore();498499if (m_address.IsSectionOffset()) {500m_address.CalculateSymbolContext(&sc);501502if (level == lldb::eDescriptionLevelFull ||503level == eDescriptionLevelInitial) {504if (IsReExported())505s->PutCString("re-exported target = ");506else507s->PutCString("where = ");508sc.DumpStopContext(s, m_owner.GetTarget().GetProcessSP().get(), m_address,509false, true, false, true, true, true);510} else {511if (sc.module_sp) {512s->EOL();513s->Indent("module = ");514sc.module_sp->GetFileSpec().Dump(s->AsRawOstream());515}516517if (sc.comp_unit != nullptr) {518s->EOL();519s->Indent("compile unit = ");520sc.comp_unit->GetPrimaryFile().GetFilename().Dump(s);521522if (sc.function != nullptr) {523s->EOL();524s->Indent("function = ");525s->PutCString(sc.function->GetName().AsCString("<unknown>"));526if (ConstString mangled_name =527sc.function->GetMangled().GetMangledName()) {528s->EOL();529s->Indent("mangled function = ");530s->PutCString(mangled_name.AsCString());531}532}533534if (sc.line_entry.line > 0) {535s->EOL();536s->Indent("location = ");537sc.line_entry.DumpStopContext(s, true);538}539540} else {541// If we don't have a comp unit, see if we have a symbol we can print.542if (sc.symbol) {543s->EOL();544if (IsReExported())545s->Indent("re-exported target = ");546else547s->Indent("symbol = ");548s->PutCString(sc.symbol->GetName().AsCString("<unknown>"));549}550}551}552}553554if (level == lldb::eDescriptionLevelVerbose) {555s->EOL();556s->Indent();557}558559if (m_address.IsSectionOffset() &&560(level == eDescriptionLevelFull || level == eDescriptionLevelInitial))561s->Printf(", ");562s->Printf("address = ");563564ExecutionContextScope *exe_scope = nullptr;565Target *target = &m_owner.GetTarget();566if (target)567exe_scope = target->GetProcessSP().get();568if (exe_scope == nullptr)569exe_scope = target;570571if (level == eDescriptionLevelInitial)572m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,573Address::DumpStyleFileAddress);574else575m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,576Address::DumpStyleModuleWithFileAddress);577578if (IsIndirect() && m_bp_site_sp) {579Address resolved_address;580resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target);581Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();582if (resolved_symbol) {583if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)584s->Printf(", ");585else if (level == lldb::eDescriptionLevelVerbose) {586s->EOL();587s->Indent();588}589s->Printf("indirect target = %s",590resolved_symbol->GetName().GetCString());591}592}593594bool is_resolved = IsResolved();595bool is_hardware = is_resolved && m_bp_site_sp->IsHardware();596597if (level == lldb::eDescriptionLevelVerbose) {598s->EOL();599s->Indent();600s->Printf("resolved = %s\n", is_resolved ? "true" : "false");601s->Indent();602s->Printf("hardware = %s\n", is_hardware ? "true" : "false");603s->Indent();604s->Printf("hit count = %-4u\n", GetHitCount());605606if (m_options_up) {607s->Indent();608m_options_up->GetDescription(s, level);609s->EOL();610}611s->IndentLess();612} else if (level != eDescriptionLevelInitial) {613s->Printf(", %sresolved, %shit count = %u ", (is_resolved ? "" : "un"),614(is_hardware ? "hardware, " : ""), GetHitCount());615if (m_options_up) {616m_options_up->GetDescription(s, level);617}618}619}620621void BreakpointLocation::Dump(Stream *s) const {622if (s == nullptr)623return;624625bool is_resolved = IsResolved();626bool is_hardware = is_resolved && m_bp_site_sp->IsHardware();627628lldb::tid_t tid = GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)629.GetThreadSpecNoCreate()630->GetTID();631s->Printf("BreakpointLocation %u: tid = %4.4" PRIx64632" load addr = 0x%8.8" PRIx64 " state = %s type = %s breakpoint "633"hit_count = %-4u ignore_count = %-4u",634GetID(), tid,635(uint64_t)m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()),636(m_options_up ? m_options_up->IsEnabled() : m_owner.IsEnabled())637? "enabled "638: "disabled",639is_hardware ? "hardware" : "software", GetHitCount(),640GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount)641.GetIgnoreCount());642}643644void BreakpointLocation::SendBreakpointLocationChangedEvent(645lldb::BreakpointEventType eventKind) {646if (!m_owner.IsInternal() && m_owner.GetTarget().EventTypeHasListeners(647Target::eBroadcastBitBreakpointChanged)) {648auto data_sp = std::make_shared<Breakpoint::BreakpointEventData>(649eventKind, m_owner.shared_from_this());650data_sp->GetBreakpointLocationCollection().Add(shared_from_this());651m_owner.GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged,652data_sp);653}654}655656void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) {657m_address = swap_from->m_address;658m_should_resolve_indirect_functions =659swap_from->m_should_resolve_indirect_functions;660m_is_reexported = swap_from->m_is_reexported;661m_is_indirect = swap_from->m_is_indirect;662m_user_expression_sp.reset();663}664665void BreakpointLocation::SetThreadIDInternal(lldb::tid_t thread_id) {666if (thread_id != LLDB_INVALID_THREAD_ID)667GetLocationOptions().SetThreadID(thread_id);668else {669// If we're resetting this to an invalid thread id, then don't make an670// options pointer just to do that.671if (m_options_up != nullptr)672m_options_up->SetThreadID(thread_id);673}674}675676677