| //===-- 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/Architecture.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.keys()) { | |
| 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) { | |
| // A breakpoint must be set on an executable instruction, not on a function's | |
| // non-instruction header. | |
| Address bp_addr = addr; | |
| if (Architecture *arch = m_target.GetArchitecturePlugin()) | |
| bp_addr = arch->SkipFunctionHeader(bp_addr); | |
| return m_locations.AddLocation(bp_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(); | |
| } | |
| StopCondition &Breakpoint::GetCondition() { 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->PutCString(", 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->PutCString("Names:"); | |
| s->EOL(); | |
| s->IndentMore(); | |
| for (llvm::StringRef name : m_name_list.keys()) { | |
| s->Indent(); | |
| s->Format("{0}\n", name); | |
| } | |
| s->IndentLess(); | |
| } | |
| s->IndentLess(); | |
| s->EOL(); | |
| } | |
| break; | |
| case lldb::eDescriptionLevelInitial: | |
| s->Printf("Breakpoint %i: ", GetID()); | |
| if (num_locations == 0) { | |
| s->PutCString("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->PutCString("Facade locations:\n"); | |
| else | |
| s->PutCString("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(llvm::StringRef 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 event_kind) { | |
| if (!IsInternal()) | |
| GetTarget().NotifyBreakpointChanged(*this, event_kind); | |
| } | |
| void Breakpoint::SendBreakpointChangedEvent( | |
| const lldb::EventDataSP &breakpoint_data_sp) { | |
| if (!breakpoint_data_sp) | |
| return; | |
| if (!IsInternal()) | |
| GetTarget().NotifyBreakpointChanged(*this, 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(); } |