|  | //===-- Breakpoint.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 "llvm/Support/Casting.h" | 
|  |  | 
|  | #include "lldb/Breakpoint/Breakpoint.h" | 
|  | #include "lldb/Breakpoint/BreakpointLocation.h" | 
|  | #include "lldb/Breakpoint/BreakpointLocationCollection.h" | 
|  | #include "lldb/Breakpoint/BreakpointPrecondition.h" | 
|  | #include "lldb/Breakpoint/BreakpointResolver.h" | 
|  | #include "lldb/Breakpoint/BreakpointResolverFileLine.h" | 
|  | #include "lldb/Core/Address.h" | 
|  | #include "lldb/Core/Debugger.h" | 
|  | #include "lldb/Core/Module.h" | 
|  | #include "lldb/Core/ModuleList.h" | 
|  | #include "lldb/Core/SearchFilter.h" | 
|  | #include "lldb/Core/Section.h" | 
|  | #include "lldb/Symbol/CompileUnit.h" | 
|  | #include "lldb/Symbol/Function.h" | 
|  | #include "lldb/Symbol/Symbol.h" | 
|  | #include "lldb/Symbol/SymbolContext.h" | 
|  | #include "lldb/Target/SectionLoadList.h" | 
|  | #include "lldb/Target/Target.h" | 
|  | #include "lldb/Target/ThreadSpec.h" | 
|  | #include "lldb/Utility/AnsiTerminal.h" | 
|  | #include "lldb/Utility/LLDBLog.h" | 
|  | #include "lldb/Utility/Log.h" | 
|  | #include "lldb/Utility/Stream.h" | 
|  | #include "lldb/Utility/StreamString.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | using namespace lldb; | 
|  | using namespace lldb_private; | 
|  | using namespace llvm; | 
|  |  | 
|  | const char *Breakpoint::g_option_names[static_cast<uint32_t>( | 
|  | Breakpoint::OptionNames::LastOptionName)]{"Names", "Hardware"}; | 
|  |  | 
|  | // Breakpoint constructor | 
|  | Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, | 
|  | BreakpointResolverSP &resolver_sp, bool hardware, | 
|  | bool resolve_indirect_symbols) | 
|  | : m_hardware(hardware), m_target(target), m_filter_sp(filter_sp), | 
|  | m_resolver_sp(resolver_sp), m_options(true), m_locations(*this), | 
|  | m_resolve_indirect_symbols(resolve_indirect_symbols), m_hit_counter() {} | 
|  |  | 
|  | Breakpoint::Breakpoint(Target &new_target, const Breakpoint &source_bp) | 
|  | : m_hardware(source_bp.m_hardware), m_target(new_target), | 
|  | m_name_list(source_bp.m_name_list), m_options(source_bp.m_options), | 
|  | m_locations(*this), | 
|  | m_resolve_indirect_symbols(source_bp.m_resolve_indirect_symbols), | 
|  | m_hit_counter() {} | 
|  |  | 
|  | // Destructor | 
|  | Breakpoint::~Breakpoint() { | 
|  | for (BreakpointLocationSP location_sp : m_locations.BreakpointLocations()) | 
|  | location_sp->SetInvalid(); | 
|  | for (BreakpointLocationSP location_sp : | 
|  | m_facade_locations.BreakpointLocations()) | 
|  | location_sp->SetInvalid(); | 
|  | } | 
|  |  | 
|  | BreakpointSP Breakpoint::CopyFromBreakpoint(TargetSP new_target, | 
|  | const Breakpoint &bp_to_copy_from) { | 
|  | if (!new_target) | 
|  | return BreakpointSP(); | 
|  |  | 
|  | BreakpointSP bp(new Breakpoint(*new_target, bp_to_copy_from)); | 
|  | // Now go through and copy the filter & resolver: | 
|  | bp->m_resolver_sp = bp_to_copy_from.m_resolver_sp->CopyForBreakpoint(bp); | 
|  | bp->m_filter_sp = bp_to_copy_from.m_filter_sp->CreateCopy(new_target); | 
|  | return bp; | 
|  | } | 
|  |  | 
|  | // Serialization | 
|  | StructuredData::ObjectSP Breakpoint::SerializeToStructuredData() { | 
|  | // Serialize the resolver: | 
|  | StructuredData::DictionarySP breakpoint_dict_sp( | 
|  | new StructuredData::Dictionary()); | 
|  | StructuredData::DictionarySP breakpoint_contents_sp( | 
|  | new StructuredData::Dictionary()); | 
|  |  | 
|  | if (!m_name_list.empty()) { | 
|  | StructuredData::ArraySP names_array_sp(new StructuredData::Array()); | 
|  | for (auto name : m_name_list) { | 
|  | names_array_sp->AddItem(std::make_shared<StructuredData::String>(name)); | 
|  | } | 
|  | breakpoint_contents_sp->AddItem(Breakpoint::GetKey(OptionNames::Names), | 
|  | names_array_sp); | 
|  | } | 
|  |  | 
|  | breakpoint_contents_sp->AddBooleanItem( | 
|  | Breakpoint::GetKey(OptionNames::Hardware), m_hardware); | 
|  |  | 
|  | StructuredData::ObjectSP resolver_dict_sp( | 
|  | m_resolver_sp->SerializeToStructuredData()); | 
|  | if (!resolver_dict_sp) | 
|  | return StructuredData::ObjectSP(); | 
|  |  | 
|  | breakpoint_contents_sp->AddItem(BreakpointResolver::GetSerializationKey(), | 
|  | resolver_dict_sp); | 
|  |  | 
|  | StructuredData::ObjectSP filter_dict_sp( | 
|  | m_filter_sp->SerializeToStructuredData()); | 
|  | if (!filter_dict_sp) | 
|  | return StructuredData::ObjectSP(); | 
|  |  | 
|  | breakpoint_contents_sp->AddItem(SearchFilter::GetSerializationKey(), | 
|  | filter_dict_sp); | 
|  |  | 
|  | StructuredData::ObjectSP options_dict_sp( | 
|  | m_options.SerializeToStructuredData()); | 
|  | if (!options_dict_sp) | 
|  | return StructuredData::ObjectSP(); | 
|  |  | 
|  | breakpoint_contents_sp->AddItem(BreakpointOptions::GetSerializationKey(), | 
|  | options_dict_sp); | 
|  |  | 
|  | breakpoint_dict_sp->AddItem(GetSerializationKey(), breakpoint_contents_sp); | 
|  | return breakpoint_dict_sp; | 
|  | } | 
|  |  | 
|  | lldb::BreakpointSP Breakpoint::CreateFromStructuredData( | 
|  | TargetSP target_sp, StructuredData::ObjectSP &object_data, Status &error) { | 
|  | BreakpointSP result_sp; | 
|  | if (!target_sp) | 
|  | return result_sp; | 
|  |  | 
|  | StructuredData::Dictionary *breakpoint_dict = object_data->GetAsDictionary(); | 
|  |  | 
|  | if (!breakpoint_dict || !breakpoint_dict->IsValid()) { | 
|  | error = Status::FromErrorString( | 
|  | "Can't deserialize from an invalid data object."); | 
|  | return result_sp; | 
|  | } | 
|  |  | 
|  | StructuredData::Dictionary *resolver_dict; | 
|  | bool success = breakpoint_dict->GetValueForKeyAsDictionary( | 
|  | BreakpointResolver::GetSerializationKey(), resolver_dict); | 
|  | if (!success) { | 
|  | error = Status::FromErrorString( | 
|  | "Breakpoint data missing toplevel resolver key"); | 
|  | return result_sp; | 
|  | } | 
|  |  | 
|  | Status create_error; | 
|  | BreakpointResolverSP resolver_sp = | 
|  | BreakpointResolver::CreateFromStructuredData(*resolver_dict, | 
|  | create_error); | 
|  | if (create_error.Fail()) { | 
|  | error = Status::FromErrorStringWithFormatv( | 
|  | "Error creating breakpoint resolver from data: {0}.", create_error); | 
|  | return result_sp; | 
|  | } | 
|  |  | 
|  | StructuredData::Dictionary *filter_dict; | 
|  | success = breakpoint_dict->GetValueForKeyAsDictionary( | 
|  | SearchFilter::GetSerializationKey(), filter_dict); | 
|  | SearchFilterSP filter_sp; | 
|  | if (!success) | 
|  | filter_sp = | 
|  | std::make_shared<SearchFilterForUnconstrainedSearches>(target_sp); | 
|  | else { | 
|  | filter_sp = SearchFilter::CreateFromStructuredData(target_sp, *filter_dict, | 
|  | create_error); | 
|  | if (create_error.Fail()) { | 
|  | error = Status::FromErrorStringWithFormat( | 
|  | "Error creating breakpoint filter from data: %s.", | 
|  | create_error.AsCString()); | 
|  | return result_sp; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::unique_ptr<BreakpointOptions> options_up; | 
|  | StructuredData::Dictionary *options_dict; | 
|  | Target &target = *target_sp; | 
|  | success = breakpoint_dict->GetValueForKeyAsDictionary( | 
|  | BreakpointOptions::GetSerializationKey(), options_dict); | 
|  | if (success) { | 
|  | options_up = BreakpointOptions::CreateFromStructuredData( | 
|  | target, *options_dict, create_error); | 
|  | if (create_error.Fail()) { | 
|  | error = Status::FromErrorStringWithFormat( | 
|  | "Error creating breakpoint options from data: %s.", | 
|  | create_error.AsCString()); | 
|  | return result_sp; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool hardware = false; | 
|  | success = breakpoint_dict->GetValueForKeyAsBoolean( | 
|  | Breakpoint::GetKey(OptionNames::Hardware), hardware); | 
|  |  | 
|  | result_sp = | 
|  | target.CreateBreakpoint(filter_sp, resolver_sp, false, hardware, true); | 
|  |  | 
|  | if (result_sp && options_up) { | 
|  | result_sp->m_options = *options_up; | 
|  | } | 
|  |  | 
|  | StructuredData::Array *names_array; | 
|  | success = breakpoint_dict->GetValueForKeyAsArray( | 
|  | Breakpoint::GetKey(OptionNames::Names), names_array); | 
|  | if (success && names_array) { | 
|  | size_t num_names = names_array->GetSize(); | 
|  | for (size_t i = 0; i < num_names; i++) { | 
|  | if (std::optional<llvm::StringRef> maybe_name = | 
|  | names_array->GetItemAtIndexAsString(i)) | 
|  | target.AddNameToBreakpoint(result_sp, *maybe_name, error); | 
|  | } | 
|  | } | 
|  |  | 
|  | return result_sp; | 
|  | } | 
|  |  | 
|  | bool Breakpoint::SerializedBreakpointMatchesNames( | 
|  | StructuredData::ObjectSP &bkpt_object_sp, std::vector<std::string> &names) { | 
|  | if (!bkpt_object_sp) | 
|  | return false; | 
|  |  | 
|  | StructuredData::Dictionary *bkpt_dict = bkpt_object_sp->GetAsDictionary(); | 
|  | if (!bkpt_dict) | 
|  | return false; | 
|  |  | 
|  | if (names.empty()) | 
|  | return true; | 
|  |  | 
|  | StructuredData::Array *names_array; | 
|  |  | 
|  | bool success = | 
|  | bkpt_dict->GetValueForKeyAsArray(GetKey(OptionNames::Names), names_array); | 
|  | // If there are no names, it can't match these names; | 
|  | if (!success) | 
|  | return false; | 
|  |  | 
|  | size_t num_names = names_array->GetSize(); | 
|  |  | 
|  | for (size_t i = 0; i < num_names; i++) { | 
|  | std::optional<llvm::StringRef> maybe_name = | 
|  | names_array->GetItemAtIndexAsString(i); | 
|  | if (maybe_name && llvm::is_contained(names, *maybe_name)) | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const lldb::TargetSP Breakpoint::GetTargetSP() { | 
|  | return m_target.shared_from_this(); | 
|  | } | 
|  |  | 
|  | bool Breakpoint::IsInternal() const { return LLDB_BREAK_ID_IS_INTERNAL(m_bid); } | 
|  |  | 
|  | llvm::Error Breakpoint::SetIsHardware(bool is_hardware) { | 
|  | if (is_hardware == m_hardware) | 
|  | return llvm::Error::success(); | 
|  |  | 
|  | Log *log = GetLog(LLDBLog::Breakpoints); | 
|  |  | 
|  | // Disable all non-hardware breakpoint locations. | 
|  | std::vector<BreakpointLocationSP> locations; | 
|  | for (BreakpointLocationSP location_sp : m_locations.BreakpointLocations()) { | 
|  | if (!location_sp || !location_sp->IsEnabled()) | 
|  | continue; | 
|  |  | 
|  | lldb::BreakpointSiteSP breakpoint_site_sp = | 
|  | location_sp->GetBreakpointSite(); | 
|  | if (!breakpoint_site_sp || | 
|  | breakpoint_site_sp->GetType() == BreakpointSite::eHardware) | 
|  | continue; | 
|  |  | 
|  | locations.push_back(location_sp); | 
|  | if (llvm::Error error = location_sp->SetEnabled(false)) | 
|  | LLDB_LOG_ERROR(log, std::move(error), | 
|  | "Failed to disable breakpoint location: {0}"); | 
|  | } | 
|  |  | 
|  | // Toggle the hardware mode. | 
|  | m_hardware = is_hardware; | 
|  |  | 
|  | // Re-enable all breakpoint locations. | 
|  | size_t num_failures = 0; | 
|  | for (BreakpointLocationSP location_sp : locations) { | 
|  | if (llvm::Error error = location_sp->SetEnabled(true)) { | 
|  | LLDB_LOG_ERROR(log, std::move(error), | 
|  | "Failed to re-enable breakpoint location: {0}"); | 
|  | num_failures++; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (num_failures != 0) | 
|  | return llvm::createStringError( | 
|  | "%ull out of %ull breakpoint locations left disabled because they " | 
|  | "couldn't be converted to hardware", | 
|  | num_failures, locations.size()); | 
|  |  | 
|  | return llvm::Error::success(); | 
|  | } | 
|  |  | 
|  | BreakpointLocationSP Breakpoint::AddLocation(const Address &addr, | 
|  | bool *new_location) { | 
|  | return m_locations.AddLocation(addr, m_resolve_indirect_symbols, | 
|  | new_location); | 
|  | } | 
|  |  | 
|  | BreakpointLocationSP Breakpoint::AddFacadeLocation() { | 
|  | size_t next_id = m_facade_locations.GetSize() + 1; | 
|  | BreakpointLocationSP break_loc_sp = | 
|  | std::make_shared<BreakpointLocation>(next_id, *this); | 
|  | break_loc_sp->m_is_facade = true; | 
|  | m_facade_locations.Add(break_loc_sp); | 
|  | return break_loc_sp; | 
|  | } | 
|  |  | 
|  | BreakpointLocationSP | 
|  | Breakpoint::GetFacadeLocationByID(lldb::break_id_t loc_id) { | 
|  | return m_facade_locations.GetByIndex(loc_id - 1); | 
|  | } | 
|  |  | 
|  | BreakpointLocationSP Breakpoint::FindLocationByAddress(const Address &addr) { | 
|  | return m_locations.FindByAddress(addr); | 
|  | } | 
|  |  | 
|  | break_id_t Breakpoint::FindLocationIDByAddress(const Address &addr) { | 
|  | return m_locations.FindIDByAddress(addr); | 
|  | } | 
|  |  | 
|  | BreakpointLocationSP Breakpoint::FindLocationByID(break_id_t bp_loc_id, | 
|  | bool use_facade) { | 
|  | if (use_facade && m_facade_locations.GetSize()) | 
|  | return GetFacadeLocationByID(bp_loc_id); | 
|  | return m_locations.FindByID(bp_loc_id); | 
|  | } | 
|  |  | 
|  | BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index, | 
|  | bool use_facade) { | 
|  | if (use_facade && m_facade_locations.GetSize() > 0) | 
|  | return m_facade_locations.GetByIndex(index); | 
|  | return m_locations.GetByIndex(index); | 
|  | } | 
|  |  | 
|  | void Breakpoint::RemoveInvalidLocations(const ArchSpec &arch) { | 
|  | // FIXME: Should we ask the scripted resolver whether any of its facade | 
|  | // locations are invalid? | 
|  | m_locations.RemoveInvalidLocations(arch); | 
|  | } | 
|  |  | 
|  | // For each of the overall options we need to decide how they propagate to the | 
|  | // location options.  This will determine the precedence of options on the | 
|  | // breakpoint vs. its locations. | 
|  |  | 
|  | // Disable at the breakpoint level should override the location settings. That | 
|  | // way you can conveniently turn off a whole breakpoint without messing up the | 
|  | // individual settings. | 
|  |  | 
|  | void Breakpoint::SetEnabled(bool enable) { | 
|  | if (enable == m_options.IsEnabled()) | 
|  | return; | 
|  |  | 
|  | m_options.SetEnabled(enable); | 
|  | if (enable) | 
|  | m_locations.ResolveAllBreakpointSites(); | 
|  | else | 
|  | m_locations.ClearAllBreakpointSites(); | 
|  |  | 
|  | SendBreakpointChangedEvent(enable ? eBreakpointEventTypeEnabled | 
|  | : eBreakpointEventTypeDisabled); | 
|  | } | 
|  |  | 
|  | bool Breakpoint::IsEnabled() { return m_options.IsEnabled(); } | 
|  |  | 
|  | void Breakpoint::SetIgnoreCount(uint32_t n) { | 
|  | if (m_options.GetIgnoreCount() == n) | 
|  | return; | 
|  |  | 
|  | m_options.SetIgnoreCount(n); | 
|  | SendBreakpointChangedEvent(eBreakpointEventTypeIgnoreChanged); | 
|  | } | 
|  |  | 
|  | void Breakpoint::DecrementIgnoreCount() { | 
|  | uint32_t ignore = m_options.GetIgnoreCount(); | 
|  | if (ignore != 0) | 
|  | m_options.SetIgnoreCount(ignore - 1); | 
|  | } | 
|  |  | 
|  | uint32_t Breakpoint::GetIgnoreCount() const { | 
|  | return m_options.GetIgnoreCount(); | 
|  | } | 
|  |  | 
|  | uint32_t Breakpoint::GetHitCount() const { return m_hit_counter.GetValue(); } | 
|  |  | 
|  | void Breakpoint::ResetHitCount() { | 
|  | m_hit_counter.Reset(); | 
|  | m_locations.ResetHitCount(); | 
|  | } | 
|  |  | 
|  | bool Breakpoint::IsOneShot() const { return m_options.IsOneShot(); } | 
|  |  | 
|  | void Breakpoint::SetOneShot(bool one_shot) { m_options.SetOneShot(one_shot); } | 
|  |  | 
|  | bool Breakpoint::IsAutoContinue() const { return m_options.IsAutoContinue(); } | 
|  |  | 
|  | void Breakpoint::SetAutoContinue(bool auto_continue) { | 
|  | m_options.SetAutoContinue(auto_continue); | 
|  | } | 
|  |  | 
|  | void Breakpoint::SetThreadID(lldb::tid_t thread_id) { | 
|  | if (m_options.GetThreadSpec()->GetTID() == thread_id) | 
|  | return; | 
|  |  | 
|  | m_options.GetThreadSpec()->SetTID(thread_id); | 
|  | SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); | 
|  | } | 
|  |  | 
|  | lldb::tid_t Breakpoint::GetThreadID() const { | 
|  | if (m_options.GetThreadSpecNoCreate() == nullptr) | 
|  | return LLDB_INVALID_THREAD_ID; | 
|  | return m_options.GetThreadSpecNoCreate()->GetTID(); | 
|  | } | 
|  |  | 
|  | void Breakpoint::SetThreadIndex(uint32_t index) { | 
|  | if (m_options.GetThreadSpec()->GetIndex() == index) | 
|  | return; | 
|  |  | 
|  | m_options.GetThreadSpec()->SetIndex(index); | 
|  | SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); | 
|  | } | 
|  |  | 
|  | uint32_t Breakpoint::GetThreadIndex() const { | 
|  | if (m_options.GetThreadSpecNoCreate() == nullptr) | 
|  | return 0; | 
|  | return m_options.GetThreadSpecNoCreate()->GetIndex(); | 
|  | } | 
|  |  | 
|  | void Breakpoint::SetThreadName(const char *thread_name) { | 
|  | if (m_options.GetThreadSpec()->GetName() != nullptr && | 
|  | ::strcmp(m_options.GetThreadSpec()->GetName(), thread_name) == 0) | 
|  | return; | 
|  |  | 
|  | m_options.GetThreadSpec()->SetName(thread_name); | 
|  | SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); | 
|  | } | 
|  |  | 
|  | const char *Breakpoint::GetThreadName() const { | 
|  | if (m_options.GetThreadSpecNoCreate() == nullptr) | 
|  | return nullptr; | 
|  | return m_options.GetThreadSpecNoCreate()->GetName(); | 
|  | } | 
|  |  | 
|  | void Breakpoint::SetQueueName(const char *queue_name) { | 
|  | if (m_options.GetThreadSpec()->GetQueueName() != nullptr && | 
|  | ::strcmp(m_options.GetThreadSpec()->GetQueueName(), queue_name) == 0) | 
|  | return; | 
|  |  | 
|  | m_options.GetThreadSpec()->SetQueueName(queue_name); | 
|  | SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); | 
|  | } | 
|  |  | 
|  | const char *Breakpoint::GetQueueName() const { | 
|  | if (m_options.GetThreadSpecNoCreate() == nullptr) | 
|  | return nullptr; | 
|  | return m_options.GetThreadSpecNoCreate()->GetQueueName(); | 
|  | } | 
|  |  | 
|  | void Breakpoint::SetCondition(StopCondition condition) { | 
|  | m_options.SetCondition(std::move(condition)); | 
|  | SendBreakpointChangedEvent(eBreakpointEventTypeConditionChanged); | 
|  | } | 
|  |  | 
|  | const StopCondition &Breakpoint::GetCondition() const { | 
|  | return m_options.GetCondition(); | 
|  | } | 
|  |  | 
|  | // This function is used when "baton" doesn't need to be freed | 
|  | void Breakpoint::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. | 
|  | m_options.SetCallback(callback, std::make_shared<UntypedBaton>(baton), | 
|  | is_synchronous); | 
|  |  | 
|  | SendBreakpointChangedEvent(eBreakpointEventTypeCommandChanged); | 
|  | } | 
|  |  | 
|  | // This function is used when a baton needs to be freed and therefore is | 
|  | // contained in a "Baton" subclass. | 
|  | void Breakpoint::SetCallback(BreakpointHitCallback callback, | 
|  | const BatonSP &callback_baton_sp, | 
|  | bool is_synchronous) { | 
|  | m_options.SetCallback(callback, callback_baton_sp, is_synchronous); | 
|  | } | 
|  |  | 
|  | void Breakpoint::ClearCallback() { m_options.ClearCallback(); } | 
|  |  | 
|  | bool Breakpoint::InvokeCallback(StoppointCallbackContext *context, | 
|  | break_id_t bp_loc_id) { | 
|  | return m_options.InvokeCallback(context, GetID(), bp_loc_id); | 
|  | } | 
|  |  | 
|  | BreakpointOptions &Breakpoint::GetOptions() { return m_options; } | 
|  |  | 
|  | const BreakpointOptions &Breakpoint::GetOptions() const { return m_options; } | 
|  |  | 
|  | void Breakpoint::ResolveBreakpoint() { | 
|  | if (m_resolver_sp) { | 
|  | ElapsedTime elapsed(m_resolve_time); | 
|  | m_resolver_sp->ResolveBreakpoint(*m_filter_sp); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Breakpoint::ResolveBreakpointInModules( | 
|  | ModuleList &module_list, BreakpointLocationCollection &new_locations) { | 
|  | ElapsedTime elapsed(m_resolve_time); | 
|  | m_locations.StartRecordingNewLocations(new_locations); | 
|  |  | 
|  | m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); | 
|  |  | 
|  | m_locations.StopRecordingNewLocations(); | 
|  | } | 
|  |  | 
|  | void Breakpoint::ResolveBreakpointInModules(ModuleList &module_list, | 
|  | bool send_event) { | 
|  | if (m_resolver_sp) { | 
|  | // If this is not an internal breakpoint, set up to record the new | 
|  | // locations, then dispatch an event with the new locations. | 
|  | if (!IsInternal() && send_event) { | 
|  | std::shared_ptr<BreakpointEventData> new_locations_event = | 
|  | std::make_shared<BreakpointEventData>( | 
|  | eBreakpointEventTypeLocationsAdded, shared_from_this()); | 
|  | ResolveBreakpointInModules( | 
|  | module_list, new_locations_event->GetBreakpointLocationCollection()); | 
|  | if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0) | 
|  | SendBreakpointChangedEvent(new_locations_event); | 
|  | } else { | 
|  | ElapsedTime elapsed(m_resolve_time); | 
|  | m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Breakpoint::ClearAllBreakpointSites() { | 
|  | m_locations.ClearAllBreakpointSites(); | 
|  | } | 
|  |  | 
|  | // ModulesChanged: Pass in a list of new modules, and | 
|  |  | 
|  | void Breakpoint::ModulesChanged(ModuleList &module_list, bool load, | 
|  | bool delete_locations) { | 
|  | Log *log = GetLog(LLDBLog::Breakpoints); | 
|  | LLDB_LOGF(log, | 
|  | "Breakpoint::ModulesChanged: num_modules: %zu load: %i " | 
|  | "delete_locations: %i\n", | 
|  | module_list.GetSize(), load, delete_locations); | 
|  |  | 
|  | if (load) { | 
|  | // The logic for handling new modules is: | 
|  | // 1) If the filter rejects this module, then skip it. 2) Run through the | 
|  | // current location list and if there are any locations | 
|  | //    for that module, we mark the module as "seen" and we don't try to | 
|  | //    re-resolve | 
|  | //    breakpoint locations for that module. | 
|  | //    However, we do add breakpoint sites to these locations if needed. | 
|  | // 3) If we don't see this module in our breakpoint location list, call | 
|  | // ResolveInModules. | 
|  |  | 
|  | ModuleList new_modules; // We'll stuff the "unseen" modules in this list, | 
|  | // and then resolve | 
|  | // them after the locations pass.  Have to do it this way because resolving | 
|  | // breakpoints will add new locations potentially. | 
|  |  | 
|  | for (ModuleSP module_sp : module_list.Modules()) { | 
|  | bool seen = false; | 
|  | if (!m_filter_sp->ModulePasses(module_sp)) | 
|  | continue; | 
|  |  | 
|  | BreakpointLocationCollection locations_with_no_section; | 
|  | for (BreakpointLocationSP break_loc_sp : | 
|  | m_locations.BreakpointLocations()) { | 
|  |  | 
|  | // If the section for this location was deleted, that means it's Module | 
|  | // has gone away but somebody forgot to tell us. Let's clean it up | 
|  | // here. | 
|  | Address section_addr(break_loc_sp->GetAddress()); | 
|  | if (section_addr.SectionWasDeleted()) { | 
|  | locations_with_no_section.Add(break_loc_sp); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!break_loc_sp->IsEnabled()) | 
|  | continue; | 
|  |  | 
|  | SectionSP section_sp(section_addr.GetSection()); | 
|  |  | 
|  | // If we don't have a Section, that means this location is a raw | 
|  | // address that we haven't resolved to a section yet.  So we'll have to | 
|  | // look in all the new modules to resolve this location. Otherwise, if | 
|  | // it was set in this module, re-resolve it here. | 
|  | if (section_sp && section_sp->GetModule() == module_sp) { | 
|  | if (!seen) | 
|  | seen = true; | 
|  |  | 
|  | if (llvm::Error error = break_loc_sp->ResolveBreakpointSite()) { | 
|  | LLDB_LOG_ERROR(log, std::move(error), | 
|  | "could not set breakpoint site for " | 
|  | "breakpoint location {1} of breakpoint {2}: {0}", | 
|  | break_loc_sp->GetID(), GetID()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t num_to_delete = locations_with_no_section.GetSize(); | 
|  |  | 
|  | for (size_t i = 0; i < num_to_delete; i++) | 
|  | m_locations.RemoveLocation(locations_with_no_section.GetByIndex(i)); | 
|  |  | 
|  | if (!seen) | 
|  | new_modules.AppendIfNeeded(module_sp); | 
|  | } | 
|  |  | 
|  | if (new_modules.GetSize() > 0) { | 
|  | ResolveBreakpointInModules(new_modules); | 
|  | } | 
|  | } else { | 
|  | // Go through the currently set locations and if any have breakpoints in | 
|  | // the module list, then remove their breakpoint sites, and their locations | 
|  | // if asked to. | 
|  |  | 
|  | std::shared_ptr<BreakpointEventData> removed_locations_event; | 
|  | if (!IsInternal()) | 
|  | removed_locations_event = std::make_shared<BreakpointEventData>( | 
|  | eBreakpointEventTypeLocationsRemoved, shared_from_this()); | 
|  |  | 
|  | for (ModuleSP module_sp : module_list.Modules()) { | 
|  | if (m_filter_sp->ModulePasses(module_sp)) { | 
|  | size_t loc_idx = 0; | 
|  | size_t num_locations = m_locations.GetSize(); | 
|  | BreakpointLocationCollection locations_to_remove; | 
|  | for (loc_idx = 0; loc_idx < num_locations; loc_idx++) { | 
|  | BreakpointLocationSP break_loc_sp(m_locations.GetByIndex(loc_idx)); | 
|  | SectionSP section_sp(break_loc_sp->GetAddress().GetSection()); | 
|  | if (section_sp && section_sp->GetModule() == module_sp) { | 
|  | // Remove this breakpoint since the shared library is unloaded, but | 
|  | // keep the breakpoint location around so we always get complete | 
|  | // hit count and breakpoint lifetime info | 
|  | if (llvm::Error error = break_loc_sp->ClearBreakpointSite()) | 
|  | LLDB_LOG_ERROR(log, std::move(error), | 
|  | "Failed to clear breakpoint locations on library " | 
|  | "unload: {0}"); | 
|  | if (removed_locations_event) { | 
|  | removed_locations_event->GetBreakpointLocationCollection().Add( | 
|  | break_loc_sp); | 
|  | } | 
|  | if (delete_locations) | 
|  | locations_to_remove.Add(break_loc_sp); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (delete_locations) { | 
|  | size_t num_locations_to_remove = locations_to_remove.GetSize(); | 
|  | for (loc_idx = 0; loc_idx < num_locations_to_remove; loc_idx++) | 
|  | m_locations.RemoveLocation(locations_to_remove.GetByIndex(loc_idx)); | 
|  | } | 
|  | } | 
|  | } | 
|  | SendBreakpointChangedEvent(removed_locations_event); | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool SymbolContextsMightBeEquivalent(SymbolContext &old_sc, | 
|  | SymbolContext &new_sc) { | 
|  | bool equivalent_scs = false; | 
|  |  | 
|  | if (old_sc.module_sp.get() == new_sc.module_sp.get()) { | 
|  | // If these come from the same module, we can directly compare the | 
|  | // pointers: | 
|  | if (old_sc.comp_unit && new_sc.comp_unit && | 
|  | (old_sc.comp_unit == new_sc.comp_unit)) { | 
|  | if (old_sc.function && new_sc.function && | 
|  | (old_sc.function == new_sc.function)) { | 
|  | equivalent_scs = true; | 
|  | } | 
|  | } else if (old_sc.symbol && new_sc.symbol && | 
|  | (old_sc.symbol == new_sc.symbol)) { | 
|  | equivalent_scs = true; | 
|  | } | 
|  | } else { | 
|  | // Otherwise we will compare by name... | 
|  | if (old_sc.comp_unit && new_sc.comp_unit) { | 
|  | if (old_sc.comp_unit->GetPrimaryFile() == | 
|  | new_sc.comp_unit->GetPrimaryFile()) { | 
|  | // Now check the functions: | 
|  | if (old_sc.function && new_sc.function && | 
|  | (old_sc.function->GetName() == new_sc.function->GetName())) { | 
|  | equivalent_scs = true; | 
|  | } | 
|  | } | 
|  | } else if (old_sc.symbol && new_sc.symbol) { | 
|  | if (Mangled::Compare(old_sc.symbol->GetMangled(), | 
|  | new_sc.symbol->GetMangled()) == 0) { | 
|  | equivalent_scs = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | return equivalent_scs; | 
|  | } | 
|  |  | 
|  | void Breakpoint::ModuleReplaced(ModuleSP old_module_sp, | 
|  | ModuleSP new_module_sp) { | 
|  | Log *log = GetLog(LLDBLog::Breakpoints); | 
|  | LLDB_LOGF(log, "Breakpoint::ModulesReplaced for %s\n", | 
|  | old_module_sp->GetSpecificationDescription().c_str()); | 
|  | // First find all the locations that are in the old module | 
|  |  | 
|  | BreakpointLocationCollection old_break_locs; | 
|  | for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations()) { | 
|  | SectionSP section_sp = break_loc_sp->GetAddress().GetSection(); | 
|  | if (section_sp && section_sp->GetModule() == old_module_sp) { | 
|  | old_break_locs.Add(break_loc_sp); | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t num_old_locations = old_break_locs.GetSize(); | 
|  |  | 
|  | if (num_old_locations == 0) { | 
|  | // There were no locations in the old module, so we just need to check if | 
|  | // there were any in the new module. | 
|  | ModuleList temp_list; | 
|  | temp_list.Append(new_module_sp); | 
|  | ResolveBreakpointInModules(temp_list); | 
|  | } else { | 
|  | // First search the new module for locations. Then compare this with the | 
|  | // old list, copy over locations that "look the same" Then delete the old | 
|  | // locations. Finally remember to post the creation event. | 
|  | // | 
|  | // Two locations are the same if they have the same comp unit & function | 
|  | // (by name) and there are the same number of locations in the old function | 
|  | // as in the new one. | 
|  |  | 
|  | ModuleList temp_list; | 
|  | temp_list.Append(new_module_sp); | 
|  | BreakpointLocationCollection new_break_locs; | 
|  | ResolveBreakpointInModules(temp_list, new_break_locs); | 
|  | BreakpointLocationCollection locations_to_remove; | 
|  | BreakpointLocationCollection locations_to_announce; | 
|  |  | 
|  | size_t num_new_locations = new_break_locs.GetSize(); | 
|  |  | 
|  | if (num_new_locations > 0) { | 
|  | // Break out the case of one location -> one location since that's the | 
|  | // most common one, and there's no need to build up the structures needed | 
|  | // for the merge in that case. | 
|  | if (num_new_locations == 1 && num_old_locations == 1) { | 
|  | bool equivalent_locations = false; | 
|  | SymbolContext old_sc, new_sc; | 
|  | // The only way the old and new location can be equivalent is if they | 
|  | // have the same amount of information: | 
|  | BreakpointLocationSP old_loc_sp = old_break_locs.GetByIndex(0); | 
|  | BreakpointLocationSP new_loc_sp = new_break_locs.GetByIndex(0); | 
|  |  | 
|  | if (old_loc_sp->GetAddress().CalculateSymbolContext(&old_sc) == | 
|  | new_loc_sp->GetAddress().CalculateSymbolContext(&new_sc)) { | 
|  | equivalent_locations = | 
|  | SymbolContextsMightBeEquivalent(old_sc, new_sc); | 
|  | } | 
|  |  | 
|  | if (equivalent_locations) { | 
|  | m_locations.SwapLocation(old_loc_sp, new_loc_sp); | 
|  | } else { | 
|  | locations_to_remove.Add(old_loc_sp); | 
|  | locations_to_announce.Add(new_loc_sp); | 
|  | } | 
|  | } else { | 
|  | // We don't want to have to keep computing the SymbolContexts for these | 
|  | // addresses over and over, so lets get them up front: | 
|  |  | 
|  | typedef std::map<lldb::break_id_t, SymbolContext> IDToSCMap; | 
|  | IDToSCMap old_sc_map; | 
|  | for (size_t idx = 0; idx < num_old_locations; idx++) { | 
|  | SymbolContext sc; | 
|  | BreakpointLocationSP bp_loc_sp = old_break_locs.GetByIndex(idx); | 
|  | lldb::break_id_t loc_id = bp_loc_sp->GetID(); | 
|  | bp_loc_sp->GetAddress().CalculateSymbolContext(&old_sc_map[loc_id]); | 
|  | } | 
|  |  | 
|  | std::map<lldb::break_id_t, SymbolContext> new_sc_map; | 
|  | for (size_t idx = 0; idx < num_new_locations; idx++) { | 
|  | SymbolContext sc; | 
|  | BreakpointLocationSP bp_loc_sp = new_break_locs.GetByIndex(idx); | 
|  | lldb::break_id_t loc_id = bp_loc_sp->GetID(); | 
|  | bp_loc_sp->GetAddress().CalculateSymbolContext(&new_sc_map[loc_id]); | 
|  | } | 
|  | // Take an element from the old Symbol Contexts | 
|  | while (old_sc_map.size() > 0) { | 
|  | lldb::break_id_t old_id = old_sc_map.begin()->first; | 
|  | SymbolContext &old_sc = old_sc_map.begin()->second; | 
|  |  | 
|  | // Count the number of entries equivalent to this SC for the old | 
|  | // list: | 
|  | std::vector<lldb::break_id_t> old_id_vec; | 
|  | old_id_vec.push_back(old_id); | 
|  |  | 
|  | IDToSCMap::iterator tmp_iter; | 
|  | for (tmp_iter = ++old_sc_map.begin(); tmp_iter != old_sc_map.end(); | 
|  | tmp_iter++) { | 
|  | if (SymbolContextsMightBeEquivalent(old_sc, tmp_iter->second)) | 
|  | old_id_vec.push_back(tmp_iter->first); | 
|  | } | 
|  |  | 
|  | // Now find all the equivalent locations in the new list. | 
|  | std::vector<lldb::break_id_t> new_id_vec; | 
|  | for (tmp_iter = new_sc_map.begin(); tmp_iter != new_sc_map.end(); | 
|  | tmp_iter++) { | 
|  | if (SymbolContextsMightBeEquivalent(old_sc, tmp_iter->second)) | 
|  | new_id_vec.push_back(tmp_iter->first); | 
|  | } | 
|  |  | 
|  | // Alright, if we have the same number of potentially equivalent | 
|  | // locations in the old and new modules, we'll just map them one to | 
|  | // one in ascending ID order (assuming the resolver's order would | 
|  | // match the equivalent ones. Otherwise, we'll dump all the old ones, | 
|  | // and just take the new ones, erasing the elements from both maps as | 
|  | // we go. | 
|  |  | 
|  | if (old_id_vec.size() == new_id_vec.size()) { | 
|  | llvm::sort(old_id_vec); | 
|  | llvm::sort(new_id_vec); | 
|  | size_t num_elements = old_id_vec.size(); | 
|  | for (size_t idx = 0; idx < num_elements; idx++) { | 
|  | BreakpointLocationSP old_loc_sp = | 
|  | old_break_locs.FindByIDPair(GetID(), old_id_vec[idx]); | 
|  | BreakpointLocationSP new_loc_sp = | 
|  | new_break_locs.FindByIDPair(GetID(), new_id_vec[idx]); | 
|  | m_locations.SwapLocation(old_loc_sp, new_loc_sp); | 
|  | old_sc_map.erase(old_id_vec[idx]); | 
|  | new_sc_map.erase(new_id_vec[idx]); | 
|  | } | 
|  | } else { | 
|  | for (lldb::break_id_t old_id : old_id_vec) { | 
|  | locations_to_remove.Add( | 
|  | old_break_locs.FindByIDPair(GetID(), old_id)); | 
|  | old_sc_map.erase(old_id); | 
|  | } | 
|  | for (lldb::break_id_t new_id : new_id_vec) { | 
|  | locations_to_announce.Add( | 
|  | new_break_locs.FindByIDPair(GetID(), new_id)); | 
|  | new_sc_map.erase(new_id); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Now remove the remaining old locations, and cons up a removed locations | 
|  | // event. Note, we don't put the new locations that were swapped with an | 
|  | // old location on the locations_to_remove list, so we don't need to worry | 
|  | // about telling the world about removing a location we didn't tell them | 
|  | // about adding. | 
|  |  | 
|  | std::shared_ptr<BreakpointEventData> removed_locations_event; | 
|  | if (!IsInternal()) | 
|  | removed_locations_event = std::make_shared<BreakpointEventData>( | 
|  | eBreakpointEventTypeLocationsRemoved, shared_from_this()); | 
|  |  | 
|  | for (BreakpointLocationSP loc_sp : | 
|  | locations_to_remove.BreakpointLocations()) { | 
|  | m_locations.RemoveLocation(loc_sp); | 
|  | if (removed_locations_event) | 
|  | removed_locations_event->GetBreakpointLocationCollection().Add(loc_sp); | 
|  | } | 
|  | SendBreakpointChangedEvent(removed_locations_event); | 
|  |  | 
|  | // And announce the new ones. | 
|  |  | 
|  | if (!IsInternal()) { | 
|  | std::shared_ptr<BreakpointEventData> added_locations_event = | 
|  | std::make_shared<BreakpointEventData>( | 
|  | eBreakpointEventTypeLocationsAdded, shared_from_this()); | 
|  | for (BreakpointLocationSP loc_sp : | 
|  | locations_to_announce.BreakpointLocations()) | 
|  | added_locations_event->GetBreakpointLocationCollection().Add(loc_sp); | 
|  |  | 
|  | SendBreakpointChangedEvent(added_locations_event); | 
|  | } | 
|  | m_locations.Compact(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Breakpoint::Dump(Stream *) {} | 
|  |  | 
|  | size_t Breakpoint::GetNumResolvedLocations(bool use_facade) const { | 
|  | // Return the number of breakpoints that are actually resolved and set down | 
|  | // in the inferior process. | 
|  | // All facade locations are considered to be resolved: | 
|  | if (use_facade) { | 
|  | size_t num_facade_locs = m_facade_locations.GetSize(); | 
|  | if (num_facade_locs) | 
|  | return num_facade_locs; | 
|  | } | 
|  | return m_locations.GetNumResolvedLocations(); | 
|  | } | 
|  |  | 
|  | bool Breakpoint::HasResolvedLocations() const { | 
|  | return GetNumResolvedLocations() > 0; | 
|  | } | 
|  |  | 
|  | size_t Breakpoint::GetNumLocations(bool use_facade) const { | 
|  | if (use_facade) { | 
|  | size_t num_facade_locs = m_facade_locations.GetSize(); | 
|  | if (num_facade_locs > 0) | 
|  | return num_facade_locs; | 
|  | } | 
|  | return m_locations.GetSize(); | 
|  | } | 
|  |  | 
|  | void Breakpoint::AddName(llvm::StringRef new_name) { | 
|  | m_name_list.insert(new_name.str()); | 
|  | } | 
|  |  | 
|  | void Breakpoint::GetDescription(Stream *s, lldb::DescriptionLevel level, | 
|  | bool show_locations) { | 
|  | assert(s != nullptr); | 
|  |  | 
|  | const bool dim_breakpoint_description = | 
|  | !IsEnabled() && s->AsRawOstream().colors_enabled(); | 
|  | if (dim_breakpoint_description) | 
|  | s->Printf("%s", ansi::FormatAnsiTerminalCodes( | 
|  | GetTarget().GetDebugger().GetDisabledAnsiPrefix()) | 
|  | .c_str()); | 
|  |  | 
|  | if (!m_kind_description.empty()) { | 
|  | if (level == eDescriptionLevelBrief) { | 
|  | s->PutCString(GetBreakpointKind()); | 
|  | return; | 
|  | } | 
|  | s->Printf("Kind: %s\n", GetBreakpointKind()); | 
|  | } | 
|  |  | 
|  | bool show_both_types = level == eDescriptionLevelVerbose && | 
|  | HasFacadeLocations() && show_locations; | 
|  | uint8_t display_mask = eDisplayFacade; | 
|  | if (show_both_types) | 
|  | display_mask |= eDisplayHeader; | 
|  |  | 
|  | GetDescriptionForType(s, level, display_mask, show_locations); | 
|  |  | 
|  | if (show_both_types) { | 
|  | display_mask = eDisplayReal | eDisplayHeader; | 
|  | GetDescriptionForType(s, level, display_mask, show_locations); | 
|  | } | 
|  | // Reset the colors back to normal if they were previously greyed out. | 
|  | if (dim_breakpoint_description) | 
|  | s->Printf("%s", ansi::FormatAnsiTerminalCodes( | 
|  | GetTarget().GetDebugger().GetDisabledAnsiSuffix()) | 
|  | .c_str()); | 
|  | } | 
|  |  | 
|  | void Breakpoint::GetDescriptionForType(Stream *s, lldb::DescriptionLevel level, | 
|  | uint8_t display_type, | 
|  | bool show_locations) { | 
|  | bool use_facade = (display_type & eDisplayFacade) != 0; | 
|  | const size_t num_locations = GetNumLocations(use_facade); | 
|  | const size_t num_resolved_locations = GetNumResolvedLocations(use_facade); | 
|  |  | 
|  | // They just made the breakpoint, they don't need to be told HOW they made | 
|  | // it... Also, we'll print the breakpoint number differently depending on | 
|  | // whether there is 1 or more locations. | 
|  | if (level != eDescriptionLevelInitial) { | 
|  | s->Printf("%i: ", GetID()); | 
|  | GetResolverDescription(s); | 
|  | GetFilterDescription(s); | 
|  | } | 
|  |  | 
|  | switch (level) { | 
|  | case lldb::eDescriptionLevelBrief: | 
|  | case lldb::eDescriptionLevelFull: | 
|  | if (num_locations > 0) { | 
|  | s->Printf(", locations = %" PRIu64, (uint64_t)num_locations); | 
|  | if (num_resolved_locations > 0) | 
|  | s->Printf(", resolved = %" PRIu64 ", hit count = %d", | 
|  | (uint64_t)num_resolved_locations, GetHitCount()); | 
|  | } else { | 
|  | // Don't print the pending notification for exception resolvers since we | 
|  | // don't generally know how to set them until the target is run. | 
|  | if (m_resolver_sp->getResolverID() != | 
|  | BreakpointResolver::ExceptionResolver) | 
|  | s->Printf(", locations = 0 (pending)"); | 
|  | } | 
|  |  | 
|  | m_options.GetDescription(s, level); | 
|  |  | 
|  | if (m_precondition_sp) | 
|  | m_precondition_sp->GetDescription(*s, level); | 
|  |  | 
|  | if (level == lldb::eDescriptionLevelFull) { | 
|  | if (!m_name_list.empty()) { | 
|  | s->EOL(); | 
|  | s->Indent(); | 
|  | s->Printf("Names:"); | 
|  | s->EOL(); | 
|  | s->IndentMore(); | 
|  | for (const std::string &name : m_name_list) { | 
|  | s->Indent(); | 
|  | s->Printf("%s\n", name.c_str()); | 
|  | } | 
|  | s->IndentLess(); | 
|  | } | 
|  | s->IndentLess(); | 
|  | s->EOL(); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case lldb::eDescriptionLevelInitial: | 
|  | s->Printf("Breakpoint %i: ", GetID()); | 
|  | if (num_locations == 0) { | 
|  | s->Printf("no locations (pending)."); | 
|  | } else if (num_locations == 1 && !show_locations) { | 
|  | // There is only one location, so we'll just print that location | 
|  | // information. | 
|  | GetLocationAtIndex(0, use_facade)->GetDescription(s, level); | 
|  | } else { | 
|  | s->Printf("%" PRIu64 " locations.", static_cast<uint64_t>(num_locations)); | 
|  | } | 
|  | s->EOL(); | 
|  | break; | 
|  |  | 
|  | case lldb::eDescriptionLevelVerbose: | 
|  | // Verbose mode does a debug dump of the breakpoint | 
|  | Dump(s); | 
|  | s->EOL(); | 
|  | // s->Indent(); | 
|  | m_options.GetDescription(s, level); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | // The brief description is just the location name (1.2 or whatever).  That's | 
|  | // pointless to show in the breakpoint's description, so suppress it. | 
|  | if (show_locations && level != lldb::eDescriptionLevelBrief) { | 
|  | if ((display_type & eDisplayHeader) != 0) { | 
|  | if ((display_type & eDisplayFacade) != 0) | 
|  | s->Printf("Facade locations:\n"); | 
|  | else | 
|  | s->Printf("Implementation Locations\n"); | 
|  | } | 
|  | s->IndentMore(); | 
|  | for (size_t i = 0; i < num_locations; ++i) { | 
|  | BreakpointLocation *loc = GetLocationAtIndex(i, use_facade).get(); | 
|  | loc->GetDescription(s, level); | 
|  | s->EOL(); | 
|  | } | 
|  | s->IndentLess(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Breakpoint::GetResolverDescription(Stream *s) { | 
|  | if (m_resolver_sp) | 
|  | m_resolver_sp->GetDescription(s); | 
|  | } | 
|  |  | 
|  | bool Breakpoint::GetMatchingFileLine(ConstString filename, uint32_t line_number, | 
|  | BreakpointLocationCollection &loc_coll) { | 
|  | // TODO: To be correct, this method needs to fill the breakpoint location | 
|  | // collection | 
|  | //       with the location IDs which match the filename and line_number. | 
|  | // | 
|  |  | 
|  | if (m_resolver_sp) { | 
|  | BreakpointResolverFileLine *resolverFileLine = | 
|  | dyn_cast<BreakpointResolverFileLine>(m_resolver_sp.get()); | 
|  |  | 
|  | // TODO: Handle SourceLocationSpec column information | 
|  | if (resolverFileLine && | 
|  | resolverFileLine->m_location_spec.GetFileSpec().GetFilename() == | 
|  | filename && | 
|  | resolverFileLine->m_location_spec.GetLine() == line_number) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void Breakpoint::GetFilterDescription(Stream *s) { | 
|  | m_filter_sp->GetDescription(s); | 
|  | } | 
|  |  | 
|  | bool Breakpoint::EvaluatePrecondition(StoppointCallbackContext &context) { | 
|  | if (!m_precondition_sp) | 
|  | return true; | 
|  |  | 
|  | return m_precondition_sp->EvaluatePrecondition(context); | 
|  | } | 
|  |  | 
|  | void Breakpoint::SendBreakpointChangedEvent( | 
|  | lldb::BreakpointEventType eventKind) { | 
|  | if (!IsInternal() && GetTarget().EventTypeHasListeners( | 
|  | Target::eBroadcastBitBreakpointChanged)) { | 
|  | std::shared_ptr<BreakpointEventData> data = | 
|  | std::make_shared<BreakpointEventData>(eventKind, shared_from_this()); | 
|  |  | 
|  | GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Breakpoint::SendBreakpointChangedEvent( | 
|  | const lldb::EventDataSP &breakpoint_data_sp) { | 
|  | if (!breakpoint_data_sp) | 
|  | return; | 
|  |  | 
|  | if (!IsInternal() && | 
|  | GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) | 
|  | GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, | 
|  | breakpoint_data_sp); | 
|  | } | 
|  |  | 
|  | const char *Breakpoint::BreakpointEventTypeAsCString(BreakpointEventType type) { | 
|  | switch (type) { | 
|  | case eBreakpointEventTypeInvalidType: | 
|  | return "invalid"; | 
|  | case eBreakpointEventTypeAdded: | 
|  | return "breakpoint added"; | 
|  | case eBreakpointEventTypeRemoved: | 
|  | return "breakpoint removed"; | 
|  | case eBreakpointEventTypeLocationsAdded: | 
|  | return "locations added"; | 
|  | case eBreakpointEventTypeLocationsRemoved: | 
|  | return "locations removed"; | 
|  | case eBreakpointEventTypeLocationsResolved: | 
|  | return "locations resolved"; | 
|  | case eBreakpointEventTypeEnabled: | 
|  | return "breakpoint enabled"; | 
|  | case eBreakpointEventTypeDisabled: | 
|  | return "breakpoint disabled"; | 
|  | case eBreakpointEventTypeCommandChanged: | 
|  | return "command changed"; | 
|  | case eBreakpointEventTypeConditionChanged: | 
|  | return "condition changed"; | 
|  | case eBreakpointEventTypeIgnoreChanged: | 
|  | return "ignore count changed"; | 
|  | case eBreakpointEventTypeThreadChanged: | 
|  | return "thread changed"; | 
|  | case eBreakpointEventTypeAutoContinueChanged: | 
|  | return "autocontinue changed"; | 
|  | }; | 
|  | llvm_unreachable("Fully covered switch above!"); | 
|  | } | 
|  |  | 
|  | Log *Breakpoint::BreakpointEventData::GetLogChannel() { | 
|  | return GetLog(LLDBLog::Breakpoints); | 
|  | } | 
|  |  | 
|  | Breakpoint::BreakpointEventData::BreakpointEventData( | 
|  | BreakpointEventType sub_type, const BreakpointSP &new_breakpoint_sp) | 
|  | : m_breakpoint_event(sub_type), m_new_breakpoint_sp(new_breakpoint_sp) {} | 
|  |  | 
|  | Breakpoint::BreakpointEventData::~BreakpointEventData() = default; | 
|  |  | 
|  | llvm::StringRef Breakpoint::BreakpointEventData::GetFlavorString() { | 
|  | return "Breakpoint::BreakpointEventData"; | 
|  | } | 
|  |  | 
|  | llvm::StringRef Breakpoint::BreakpointEventData::GetFlavor() const { | 
|  | return BreakpointEventData::GetFlavorString(); | 
|  | } | 
|  |  | 
|  | BreakpointSP Breakpoint::BreakpointEventData::GetBreakpoint() const { | 
|  | return m_new_breakpoint_sp; | 
|  | } | 
|  |  | 
|  | BreakpointEventType | 
|  | Breakpoint::BreakpointEventData::GetBreakpointEventType() const { | 
|  | return m_breakpoint_event; | 
|  | } | 
|  |  | 
|  | void Breakpoint::BreakpointEventData::Dump(Stream *s) const { | 
|  | if (!s) | 
|  | return; | 
|  | BreakpointEventType event_type = GetBreakpointEventType(); | 
|  | break_id_t bkpt_id = GetBreakpoint()->GetID(); | 
|  | s->Format("bkpt: {0} type: {1}", bkpt_id, | 
|  | BreakpointEventTypeAsCString(event_type)); | 
|  | } | 
|  |  | 
|  | const Breakpoint::BreakpointEventData * | 
|  | Breakpoint::BreakpointEventData::GetEventDataFromEvent(const Event *event) { | 
|  | if (event) { | 
|  | const EventData *event_data = event->GetData(); | 
|  | if (event_data && | 
|  | event_data->GetFlavor() == BreakpointEventData::GetFlavorString()) | 
|  | return static_cast<const BreakpointEventData *>(event->GetData()); | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | BreakpointEventType | 
|  | Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent( | 
|  | const EventSP &event_sp) { | 
|  | const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); | 
|  |  | 
|  | if (data == nullptr) | 
|  | return eBreakpointEventTypeInvalidType; | 
|  | return data->GetBreakpointEventType(); | 
|  | } | 
|  |  | 
|  | BreakpointSP Breakpoint::BreakpointEventData::GetBreakpointFromEvent( | 
|  | const EventSP &event_sp) { | 
|  | BreakpointSP bp_sp; | 
|  |  | 
|  | const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); | 
|  | if (data) | 
|  | bp_sp = data->m_new_breakpoint_sp; | 
|  |  | 
|  | return bp_sp; | 
|  | } | 
|  |  | 
|  | size_t Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent( | 
|  | const EventSP &event_sp) { | 
|  | const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); | 
|  | if (data) | 
|  | return data->m_locations.GetSize(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | lldb::BreakpointLocationSP | 
|  | Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent( | 
|  | const lldb::EventSP &event_sp, uint32_t bp_loc_idx) { | 
|  | lldb::BreakpointLocationSP bp_loc_sp; | 
|  |  | 
|  | const BreakpointEventData *data = GetEventDataFromEvent(event_sp.get()); | 
|  | if (data) { | 
|  | bp_loc_sp = data->m_locations.GetByIndex(bp_loc_idx); | 
|  | } | 
|  |  | 
|  | return bp_loc_sp; | 
|  | } | 
|  |  | 
|  | json::Value Breakpoint::GetStatistics() { | 
|  | json::Object bp; | 
|  | bp.try_emplace("id", GetID()); | 
|  | bp.try_emplace("resolveTime", m_resolve_time.get().count()); | 
|  | bp.try_emplace("numLocations", (int64_t)GetNumLocations()); | 
|  | bp.try_emplace("numResolvedLocations", (int64_t)GetNumResolvedLocations()); | 
|  | bp.try_emplace("hitCount", (int64_t)GetHitCount()); | 
|  | bp.try_emplace("internal", IsInternal()); | 
|  | if (!m_kind_description.empty()) | 
|  | bp.try_emplace("kindDescription", m_kind_description); | 
|  | // Put the full structured data for reproducing this breakpoint in a key/value | 
|  | // pair named "details". This allows the breakpoint's details to be visible | 
|  | // in the stats in case we need to reproduce a breakpoint that has long | 
|  | // resolve times | 
|  | StructuredData::ObjectSP bp_data_sp = SerializeToStructuredData(); | 
|  | if (bp_data_sp) { | 
|  | std::string buffer; | 
|  | llvm::raw_string_ostream ss(buffer); | 
|  | json::OStream json_os(ss); | 
|  | bp_data_sp->Serialize(json_os); | 
|  | if (auto expected_value = llvm::json::parse(buffer)) { | 
|  | bp.try_emplace("details", std::move(*expected_value)); | 
|  | } else { | 
|  | std::string details_error = toString(expected_value.takeError()); | 
|  | json::Object details; | 
|  | details.try_emplace("error", details_error); | 
|  | bp.try_emplace("details", std::move(details)); | 
|  | } | 
|  | } | 
|  | return json::Value(std::move(bp)); | 
|  | } | 
|  |  | 
|  | void Breakpoint::ResetStatistics() { m_resolve_time.reset(); } |