aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Breakpoint
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Breakpoint')
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp1139
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp117
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp294
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp201
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp675
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp189
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp317
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp79
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp653
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp26
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp361
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp176
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp340
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp168
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp423
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp151
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp206
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/StopPointSiteList.cpp37
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp24
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp23
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/StoppointSite.cpp22
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp550
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/WatchpointAlgorithms.cpp158
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp254
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp180
-rw-r--r--contrib/llvm-project/lldb/source/Breakpoint/WatchpointResource.cpp123
26 files changed, 6886 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp b/contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp
new file mode 100644
index 000000000000..dc80d435ad44
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/Breakpoint.cpp
@@ -0,0 +1,1139 @@
+//===-- 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/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/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() = default;
+
+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(
+ StructuredData::StringSP(new 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.SetErrorString("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.SetErrorString("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.SetErrorStringWithFormat(
+ "Error creating breakpoint resolver from data: %s.",
+ create_error.AsCString());
+ 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.SetErrorStringWithFormat(
+ "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.SetErrorStringWithFormat(
+ "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); }
+
+BreakpointLocationSP Breakpoint::AddLocation(const Address &addr,
+ bool *new_location) {
+ return m_locations.AddLocation(addr, m_resolve_indirect_symbols,
+ new_location);
+}
+
+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) {
+ return m_locations.FindByID(bp_loc_id);
+}
+
+BreakpointLocationSP Breakpoint::GetLocationAtIndex(size_t index) {
+ return m_locations.GetByIndex(index);
+}
+
+void Breakpoint::RemoveInvalidLocations(const ArchSpec &arch) {
+ 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;
+ else
+ 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;
+ else
+ 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;
+ else
+ 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;
+ else
+ return m_options.GetThreadSpecNoCreate()->GetQueueName();
+}
+
+void Breakpoint::SetCondition(const char *condition) {
+ m_options.SetCondition(condition);
+ SendBreakpointChangedEvent(eBreakpointEventTypeConditionChanged);
+}
+
+const char *Breakpoint::GetConditionText() const {
+ return m_options.GetConditionText();
+}
+
+// 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 (!break_loc_sp->ResolveBreakpointSite()) {
+ LLDB_LOGF(log,
+ "Warning: could not set breakpoint site for "
+ "breakpoint location %d of breakpoint %d.\n",
+ 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
+ break_loc_sp->ClearBreakpointSite();
+ 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() const {
+ // Return the number of breakpoints that are actually resolved and set down
+ // in the inferior process.
+ return m_locations.GetNumResolvedLocations();
+}
+
+bool Breakpoint::HasResolvedLocations() const {
+ return GetNumResolvedLocations() > 0;
+}
+
+size_t Breakpoint::GetNumLocations() const { 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);
+
+ if (!m_kind_description.empty()) {
+ if (level == eDescriptionLevelBrief) {
+ s->PutCString(GetBreakpointKind());
+ return;
+ } else
+ s->Printf("Kind: %s\n", GetBreakpointKind());
+ }
+
+ const size_t num_locations = GetNumLocations();
+ const size_t num_resolved_locations = GetNumResolvedLocations();
+
+ // 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)->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) {
+ s->IndentMore();
+ for (size_t i = 0; i < num_locations; ++i) {
+ BreakpointLocation *loc = GetLocationAtIndex(i).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;
+ else
+ 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(ss.str())) {
+ 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));
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp
new file mode 100644
index 000000000000..b4a0a22f66c1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointID.cpp
@@ -0,0 +1,117 @@
+//===-- BreakpointID.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 <cstdio>
+#include <optional>
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointID.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointID::BreakpointID(break_id_t bp_id, break_id_t loc_id)
+ : m_break_id(bp_id), m_location_id(loc_id) {}
+
+BreakpointID::~BreakpointID() = default;
+
+static llvm::StringRef g_range_specifiers[] = {"-", "to", "To", "TO"};
+
+// Tells whether or not STR is valid to use between two strings representing
+// breakpoint IDs, to indicate a range of breakpoint IDs. This is broken out
+// into a separate function so that we can easily change or add to the format
+// for specifying ID ranges at a later date.
+
+bool BreakpointID::IsRangeIdentifier(llvm::StringRef str) {
+ return llvm::is_contained(g_range_specifiers, str);
+}
+
+bool BreakpointID::IsValidIDExpression(llvm::StringRef str) {
+ return BreakpointID::ParseCanonicalReference(str).has_value();
+}
+
+llvm::ArrayRef<llvm::StringRef> BreakpointID::GetRangeSpecifiers() {
+ return llvm::ArrayRef(g_range_specifiers);
+}
+
+void BreakpointID::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ if (level == eDescriptionLevelVerbose)
+ s->Printf("%p BreakpointID:", static_cast<void *>(this));
+
+ if (m_break_id == LLDB_INVALID_BREAK_ID)
+ s->PutCString("<invalid>");
+ else if (m_location_id == LLDB_INVALID_BREAK_ID)
+ s->Printf("%i", m_break_id);
+ else
+ s->Printf("%i.%i", m_break_id, m_location_id);
+}
+
+void BreakpointID::GetCanonicalReference(Stream *s, break_id_t bp_id,
+ break_id_t loc_id) {
+ if (bp_id == LLDB_INVALID_BREAK_ID)
+ s->PutCString("<invalid>");
+ else if (loc_id == LLDB_INVALID_BREAK_ID)
+ s->Printf("%i", bp_id);
+ else
+ s->Printf("%i.%i", bp_id, loc_id);
+}
+
+std::optional<BreakpointID>
+BreakpointID::ParseCanonicalReference(llvm::StringRef input) {
+ break_id_t bp_id;
+ break_id_t loc_id = LLDB_INVALID_BREAK_ID;
+
+ if (input.empty())
+ return std::nullopt;
+
+ // If it doesn't start with an integer, it's not valid.
+ if (input.consumeInteger(0, bp_id))
+ return std::nullopt;
+
+ // period is optional, but if it exists, it must be followed by a number.
+ if (input.consume_front(".")) {
+ if (input.consumeInteger(0, loc_id))
+ return std::nullopt;
+ }
+
+ // And at the end, the entire string must have been consumed.
+ if (!input.empty())
+ return std::nullopt;
+
+ return BreakpointID(bp_id, loc_id);
+}
+
+bool BreakpointID::StringIsBreakpointName(llvm::StringRef str, Status &error) {
+ error.Clear();
+ if (str.empty())
+ {
+ error.SetErrorString("Empty breakpoint names are not allowed");
+ return false;
+ }
+
+ // First character must be a letter or _
+ if (!isalpha(str[0]) && str[0] != '_')
+ {
+ error.SetErrorStringWithFormat("Breakpoint names must start with a "
+ "character or underscore: %s",
+ str.str().c_str());
+ return false;
+ }
+
+ // Cannot contain ., -, or space.
+ if (str.find_first_of(".- ") != llvm::StringRef::npos) {
+ error.SetErrorStringWithFormat("Breakpoint names cannot contain "
+ "'.' or '-' or spaces: \"%s\"",
+ str.str().c_str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp
new file mode 100644
index 000000000000..5fc9f95a75db
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointIDList.cpp
@@ -0,0 +1,294 @@
+//===-- BreakpointIDList.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/lldb-enumerations.h"
+#include "lldb/Breakpoint/BreakpointIDList.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/StreamString.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// class BreakpointIDList
+
+BreakpointIDList::BreakpointIDList() : m_breakpoint_ids() {}
+
+BreakpointIDList::~BreakpointIDList() = default;
+
+size_t BreakpointIDList::GetSize() const { return m_breakpoint_ids.size(); }
+
+BreakpointID BreakpointIDList::GetBreakpointIDAtIndex(size_t index) const {
+ return ((index < m_breakpoint_ids.size()) ? m_breakpoint_ids[index]
+ : BreakpointID());
+}
+
+bool BreakpointIDList::RemoveBreakpointIDAtIndex(size_t index) {
+ if (index >= m_breakpoint_ids.size())
+ return false;
+
+ m_breakpoint_ids.erase(m_breakpoint_ids.begin() + index);
+ return true;
+}
+
+void BreakpointIDList::Clear() { m_breakpoint_ids.clear(); }
+
+bool BreakpointIDList::AddBreakpointID(BreakpointID bp_id) {
+ m_breakpoint_ids.push_back(bp_id);
+
+ return true; // We don't do any verification in this function, so always
+ // return true.
+}
+
+bool BreakpointIDList::Contains(BreakpointID bp_id) const {
+ return llvm::is_contained(m_breakpoint_ids, bp_id);
+}
+
+// This function takes OLD_ARGS, which is usually the result of breaking the
+// command string arguments into
+// an array of space-separated strings, and searches through the arguments for
+// any breakpoint ID range specifiers.
+// Any string in the array that is not part of an ID range specifier is copied
+// directly into NEW_ARGS. If any
+// ID range specifiers are found, the range is interpreted and a list of
+// canonical breakpoint IDs corresponding to
+// all the current breakpoints and locations in the range are added to
+// NEW_ARGS. When this function is done,
+// NEW_ARGS should be a copy of OLD_ARGS, with and ID range specifiers replaced
+// by the members of the range.
+
+llvm::Error BreakpointIDList::FindAndReplaceIDRanges(
+ Args &old_args, Target *target, bool allow_locations,
+ BreakpointName::Permissions ::PermissionKinds purpose, Args &new_args) {
+ llvm::StringRef range_from;
+ llvm::StringRef range_to;
+ llvm::StringRef current_arg;
+ std::set<std::string> names_found;
+
+ for (size_t i = 0; i < old_args.size(); ++i) {
+ bool is_range = false;
+
+ current_arg = old_args[i].ref();
+ if (!allow_locations && current_arg.contains('.')) {
+ new_args.Clear();
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Breakpoint locations not allowed, saw location: %s.",
+ current_arg.str().c_str());
+ }
+
+ Status error;
+
+ std::tie(range_from, range_to) =
+ BreakpointIDList::SplitIDRangeExpression(current_arg);
+ if (!range_from.empty() && !range_to.empty()) {
+ is_range = true;
+ } else if (BreakpointID::StringIsBreakpointName(current_arg, error)) {
+ if (!error.Success()) {
+ new_args.Clear();
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ error.AsCString());
+ } else
+ names_found.insert(std::string(current_arg));
+ } else if ((i + 2 < old_args.size()) &&
+ BreakpointID::IsRangeIdentifier(old_args[i + 1].ref()) &&
+ BreakpointID::IsValidIDExpression(current_arg) &&
+ BreakpointID::IsValidIDExpression(old_args[i + 2].ref())) {
+ range_from = current_arg;
+ range_to = old_args[i + 2].ref();
+ is_range = true;
+ i = i + 2;
+ } else {
+ // See if user has specified id.*
+ llvm::StringRef tmp_str = old_args[i].ref();
+ auto [prefix, suffix] = tmp_str.split('.');
+ if (suffix == "*" && BreakpointID::IsValidIDExpression(prefix)) {
+
+ BreakpointSP breakpoint_sp;
+ auto bp_id = BreakpointID::ParseCanonicalReference(prefix);
+ if (bp_id)
+ breakpoint_sp = target->GetBreakpointByID(bp_id->GetBreakpointID());
+ if (!breakpoint_sp) {
+ new_args.Clear();
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "'%d' is not a valid breakpoint ID.\n",
+ bp_id->GetBreakpointID());
+ }
+ const size_t num_locations = breakpoint_sp->GetNumLocations();
+ for (size_t j = 0; j < num_locations; ++j) {
+ BreakpointLocation *bp_loc =
+ breakpoint_sp->GetLocationAtIndex(j).get();
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(
+ &canonical_id_str, bp_id->GetBreakpointID(), bp_loc->GetID());
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ }
+
+ if (!is_range) {
+ new_args.AppendArgument(current_arg);
+ continue;
+ }
+
+ auto start_bp = BreakpointID::ParseCanonicalReference(range_from);
+ auto end_bp = BreakpointID::ParseCanonicalReference(range_to);
+
+ if (!start_bp ||
+ !target->GetBreakpointByID(start_bp->GetBreakpointID())) {
+ new_args.Clear();
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "'%s' is not a valid breakpoint ID.\n",
+ range_from.str().c_str());
+ }
+
+ if (!end_bp ||
+ !target->GetBreakpointByID(end_bp->GetBreakpointID())) {
+ new_args.Clear();
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "'%s' is not a valid breakpoint ID.\n",
+ range_to.str().c_str());
+ }
+ break_id_t start_bp_id = start_bp->GetBreakpointID();
+ break_id_t start_loc_id = start_bp->GetLocationID();
+ break_id_t end_bp_id = end_bp->GetBreakpointID();
+ break_id_t end_loc_id = end_bp->GetLocationID();
+ if (((start_loc_id == LLDB_INVALID_BREAK_ID) &&
+ (end_loc_id != LLDB_INVALID_BREAK_ID)) ||
+ ((start_loc_id != LLDB_INVALID_BREAK_ID) &&
+ (end_loc_id == LLDB_INVALID_BREAK_ID))) {
+ new_args.Clear();
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Invalid breakpoint id range: Either "
+ "both ends of range must specify"
+ " a breakpoint location, or neither can "
+ "specify a breakpoint location.");
+ }
+
+ // We have valid range starting & ending breakpoint IDs. Go through all
+ // the breakpoints in the target and find all the breakpoints that fit into
+ // this range, and add them to new_args.
+
+ // Next check to see if we have location id's. If so, make sure the
+ // start_bp_id and end_bp_id are for the same breakpoint; otherwise we have
+ // an illegal range: breakpoint id ranges that specify bp locations are NOT
+ // allowed to cross major bp id numbers.
+
+ if ((start_loc_id != LLDB_INVALID_BREAK_ID) ||
+ (end_loc_id != LLDB_INVALID_BREAK_ID)) {
+ if (start_bp_id != end_bp_id) {
+ new_args.Clear();
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Invalid range: Ranges that specify particular breakpoint "
+ "locations"
+ " must be within the same major breakpoint; you specified two"
+ " different major breakpoints, %d and %d.\n",
+ start_bp_id, end_bp_id);
+ }
+ }
+
+ const BreakpointList &breakpoints = target->GetBreakpointList();
+ const size_t num_breakpoints = breakpoints.GetSize();
+ for (size_t j = 0; j < num_breakpoints; ++j) {
+ Breakpoint *breakpoint = breakpoints.GetBreakpointAtIndex(j).get();
+ break_id_t cur_bp_id = breakpoint->GetID();
+
+ if ((cur_bp_id < start_bp_id) || (cur_bp_id > end_bp_id))
+ continue;
+
+ const size_t num_locations = breakpoint->GetNumLocations();
+
+ if ((cur_bp_id == start_bp_id) &&
+ (start_loc_id != LLDB_INVALID_BREAK_ID)) {
+ for (size_t k = 0; k < num_locations; ++k) {
+ BreakpointLocation *bp_loc = breakpoint->GetLocationAtIndex(k).get();
+ if ((bp_loc->GetID() >= start_loc_id) &&
+ (bp_loc->GetID() <= end_loc_id)) {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
+ bp_loc->GetID());
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ } else if ((cur_bp_id == end_bp_id) &&
+ (end_loc_id != LLDB_INVALID_BREAK_ID)) {
+ for (size_t k = 0; k < num_locations; ++k) {
+ BreakpointLocation *bp_loc = breakpoint->GetLocationAtIndex(k).get();
+ if (bp_loc->GetID() <= end_loc_id) {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
+ bp_loc->GetID());
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ } else {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(&canonical_id_str, cur_bp_id,
+ LLDB_INVALID_BREAK_ID);
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ }
+
+ // Okay, now see if we found any names, and if we did, add them:
+ if (target && !names_found.empty()) {
+ Status error;
+ // Remove any names that aren't visible for this purpose:
+ auto iter = names_found.begin();
+ while (iter != names_found.end()) {
+ BreakpointName *bp_name = target->FindBreakpointName(ConstString(*iter),
+ true,
+ error);
+ if (bp_name && !bp_name->GetPermission(purpose))
+ iter = names_found.erase(iter);
+ else
+ iter++;
+ }
+
+ if (!names_found.empty()) {
+ for (BreakpointSP bkpt_sp : target->GetBreakpointList().Breakpoints()) {
+ for (const std::string &name : names_found) {
+ if (bkpt_sp->MatchesName(name.c_str())) {
+ StreamString canonical_id_str;
+ BreakpointID::GetCanonicalReference(
+ &canonical_id_str, bkpt_sp->GetID(), LLDB_INVALID_BREAK_ID);
+ new_args.AppendArgument(canonical_id_str.GetString());
+ }
+ }
+ }
+ }
+ }
+ return llvm::Error::success();
+}
+
+std::pair<llvm::StringRef, llvm::StringRef>
+BreakpointIDList::SplitIDRangeExpression(llvm::StringRef in_string) {
+ for (auto specifier_str : BreakpointID::GetRangeSpecifiers()) {
+ size_t idx = in_string.find(specifier_str);
+ if (idx == llvm::StringRef::npos)
+ continue;
+ llvm::StringRef right1 = in_string.drop_front(idx);
+
+ llvm::StringRef from = in_string.take_front(idx);
+ llvm::StringRef to = right1.drop_front(specifier_str.size());
+
+ if (BreakpointID::IsValidIDExpression(from) &&
+ BreakpointID::IsValidIDExpression(to)) {
+ return std::make_pair(from, to);
+ }
+ }
+
+ return std::pair<llvm::StringRef, llvm::StringRef>();
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp
new file mode 100644
index 000000000000..2c47b3b1263c
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointList.cpp
@@ -0,0 +1,201 @@
+//===-- BreakpointList.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointList.h"
+
+#include "lldb/Target/Target.h"
+
+#include "llvm/Support/Errc.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void NotifyChange(const BreakpointSP &bp, BreakpointEventType event) {
+ Target &target = bp->GetTarget();
+ if (target.EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) {
+ auto event_data_sp =
+ std::make_shared<Breakpoint::BreakpointEventData>(event, bp);
+ target.BroadcastEvent(Target::eBroadcastBitBreakpointChanged,
+ event_data_sp);
+ }
+}
+
+BreakpointList::BreakpointList(bool is_internal)
+ : m_next_break_id(0), m_is_internal(is_internal) {}
+
+BreakpointList::~BreakpointList() = default;
+
+break_id_t BreakpointList::Add(BreakpointSP &bp_sp, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ // Internal breakpoint IDs are negative, normal ones are positive
+ bp_sp->SetID(m_is_internal ? --m_next_break_id : ++m_next_break_id);
+
+ m_breakpoints.push_back(bp_sp);
+
+ if (notify)
+ NotifyChange(bp_sp, eBreakpointEventTypeAdded);
+
+ return bp_sp->GetID();
+}
+
+bool BreakpointList::Remove(break_id_t break_id, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ auto it = std::find_if(
+ m_breakpoints.begin(), m_breakpoints.end(),
+ [&](const BreakpointSP &bp) { return bp->GetID() == break_id; });
+
+ if (it == m_breakpoints.end())
+ return false;
+
+ if (notify)
+ NotifyChange(*it, eBreakpointEventTypeRemoved);
+
+ m_breakpoints.erase(it);
+
+ return true;
+}
+
+void BreakpointList::RemoveInvalidLocations(const ArchSpec &arch) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->RemoveInvalidLocations(arch);
+}
+
+void BreakpointList::SetEnabledAll(bool enabled) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->SetEnabled(enabled);
+}
+
+void BreakpointList::SetEnabledAllowed(bool enabled) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ if (bp_sp->AllowDisable())
+ bp_sp->SetEnabled(enabled);
+}
+
+void BreakpointList::RemoveAll(bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ ClearAllBreakpointSites();
+
+ if (notify) {
+ for (const auto &bp_sp : m_breakpoints)
+ NotifyChange(bp_sp, eBreakpointEventTypeRemoved);
+ }
+
+ m_breakpoints.clear();
+}
+
+void BreakpointList::RemoveAllowed(bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ for (const auto &bp_sp : m_breakpoints) {
+ if (bp_sp->AllowDelete())
+ bp_sp->ClearAllBreakpointSites();
+ if (notify)
+ NotifyChange(bp_sp, eBreakpointEventTypeRemoved);
+ }
+
+ llvm::erase_if(m_breakpoints,
+ [&](const BreakpointSP &bp) { return bp->AllowDelete(); });
+}
+
+BreakpointList::bp_collection::iterator
+BreakpointList::GetBreakpointIDIterator(break_id_t break_id) {
+ return std::find_if(
+ m_breakpoints.begin(), m_breakpoints.end(),
+ [&](const BreakpointSP &bp) { return bp->GetID() == break_id; });
+}
+
+BreakpointList::bp_collection::const_iterator
+BreakpointList::GetBreakpointIDConstIterator(break_id_t break_id) const {
+ return std::find_if(
+ m_breakpoints.begin(), m_breakpoints.end(),
+ [&](const BreakpointSP &bp) { return bp->GetID() == break_id; });
+}
+
+BreakpointSP BreakpointList::FindBreakpointByID(break_id_t break_id) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ auto it = GetBreakpointIDConstIterator(break_id);
+ if (it != m_breakpoints.end())
+ return *it;
+ return {};
+}
+
+llvm::Expected<std::vector<lldb::BreakpointSP>>
+BreakpointList::FindBreakpointsByName(const char *name) {
+ if (!name)
+ return llvm::createStringError(llvm::errc::invalid_argument,
+ "FindBreakpointsByName requires a name");
+
+ Status error;
+ if (!BreakpointID::StringIsBreakpointName(llvm::StringRef(name), error))
+ return error.ToError();
+
+ std::vector<lldb::BreakpointSP> matching_bps;
+ for (BreakpointSP bkpt_sp : Breakpoints()) {
+ if (bkpt_sp->MatchesName(name)) {
+ matching_bps.push_back(bkpt_sp);
+ }
+ }
+
+ return matching_bps;
+}
+
+void BreakpointList::Dump(Stream *s) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ s->Printf("%p: ", static_cast<const void *>(this));
+ s->Indent();
+ s->Printf("BreakpointList with %u Breakpoints:\n",
+ (uint32_t)m_breakpoints.size());
+ s->IndentMore();
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->Dump(s);
+ s->IndentLess();
+}
+
+BreakpointSP BreakpointList::GetBreakpointAtIndex(size_t i) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (i < m_breakpoints.size())
+ return m_breakpoints[i];
+ return {};
+}
+
+void BreakpointList::UpdateBreakpoints(ModuleList &module_list, bool added,
+ bool delete_locations) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->ModulesChanged(module_list, added, delete_locations);
+}
+
+void BreakpointList::UpdateBreakpointsWhenModuleIsReplaced(
+ ModuleSP old_module_sp, ModuleSP new_module_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->ModuleReplaced(old_module_sp, new_module_sp);
+}
+
+void BreakpointList::ClearAllBreakpointSites() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->ClearAllBreakpointSites();
+}
+
+void BreakpointList::ResetHitCounts() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (const auto &bp_sp : m_breakpoints)
+ bp_sp->ResetHitCount();
+}
+
+void BreakpointList::GetListMutex(
+ std::unique_lock<std::recursive_mutex> &lock) {
+ lock = std::unique_lock<std::recursive_mutex>(m_mutex);
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp
new file mode 100644
index 000000000000..41911fad41c6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -0,0 +1,675 @@
+//===-- BreakpointLocation.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointID.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/ExpressionVariable.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointLocation::BreakpointLocation(break_id_t loc_id, Breakpoint &owner,
+ const Address &addr, lldb::tid_t tid,
+ bool hardware, bool check_for_resolver)
+ : m_should_resolve_indirect_functions(false), m_is_reexported(false),
+ m_is_indirect(false), m_address(addr), m_owner(owner),
+ m_condition_hash(0), m_loc_id(loc_id), m_hit_counter() {
+ if (check_for_resolver) {
+ Symbol *symbol = m_address.CalculateSymbolContextSymbol();
+ if (symbol && symbol->IsIndirect()) {
+ SetShouldResolveIndirectFunctions(true);
+ }
+ }
+
+ SetThreadIDInternal(tid);
+}
+
+BreakpointLocation::~BreakpointLocation() { ClearBreakpointSite(); }
+
+lldb::addr_t BreakpointLocation::GetLoadAddress() const {
+ return m_address.GetOpcodeLoadAddress(&m_owner.GetTarget());
+}
+
+const BreakpointOptions &BreakpointLocation::GetOptionsSpecifyingKind(
+ BreakpointOptions::OptionKind kind) const {
+ if (m_options_up && m_options_up->IsOptionSet(kind))
+ return *m_options_up;
+ else
+ return m_owner.GetOptions();
+}
+
+Address &BreakpointLocation::GetAddress() { return m_address; }
+
+Breakpoint &BreakpointLocation::GetBreakpoint() { return m_owner; }
+
+Target &BreakpointLocation::GetTarget() { return m_owner.GetTarget(); }
+
+bool BreakpointLocation::IsEnabled() const {
+ if (!m_owner.IsEnabled())
+ return false;
+ else if (m_options_up != nullptr)
+ return m_options_up->IsEnabled();
+ else
+ return true;
+}
+
+void BreakpointLocation::SetEnabled(bool enabled) {
+ GetLocationOptions().SetEnabled(enabled);
+ if (enabled) {
+ ResolveBreakpointSite();
+ } else {
+ ClearBreakpointSite();
+ }
+ SendBreakpointLocationChangedEvent(enabled ? eBreakpointEventTypeEnabled
+ : eBreakpointEventTypeDisabled);
+}
+
+bool BreakpointLocation::IsAutoContinue() const {
+ if (m_options_up &&
+ m_options_up->IsOptionSet(BreakpointOptions::eAutoContinue))
+ return m_options_up->IsAutoContinue();
+ else
+ return m_owner.IsAutoContinue();
+}
+
+void BreakpointLocation::SetAutoContinue(bool auto_continue) {
+ GetLocationOptions().SetAutoContinue(auto_continue);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeAutoContinueChanged);
+}
+
+void BreakpointLocation::SetThreadID(lldb::tid_t thread_id) {
+ SetThreadIDInternal(thread_id);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+lldb::tid_t BreakpointLocation::GetThreadID() {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ .GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetTID();
+ else
+ return LLDB_INVALID_THREAD_ID;
+}
+
+void BreakpointLocation::SetThreadIndex(uint32_t index) {
+ if (index != 0)
+ GetLocationOptions().GetThreadSpec()->SetIndex(index);
+ else {
+ // If we're resetting this to an invalid thread id, then don't make an
+ // options pointer just to do that.
+ if (m_options_up != nullptr)
+ m_options_up->GetThreadSpec()->SetIndex(index);
+ }
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+uint32_t BreakpointLocation::GetThreadIndex() const {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ .GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetIndex();
+ else
+ return 0;
+}
+
+void BreakpointLocation::SetThreadName(const char *thread_name) {
+ if (thread_name != nullptr)
+ GetLocationOptions().GetThreadSpec()->SetName(thread_name);
+ else {
+ // If we're resetting this to an invalid thread id, then don't make an
+ // options pointer just to do that.
+ if (m_options_up != nullptr)
+ m_options_up->GetThreadSpec()->SetName(thread_name);
+ }
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+const char *BreakpointLocation::GetThreadName() const {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ .GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetName();
+ else
+ return nullptr;
+}
+
+void BreakpointLocation::SetQueueName(const char *queue_name) {
+ if (queue_name != nullptr)
+ GetLocationOptions().GetThreadSpec()->SetQueueName(queue_name);
+ else {
+ // If we're resetting this to an invalid thread id, then don't make an
+ // options pointer just to do that.
+ if (m_options_up != nullptr)
+ m_options_up->GetThreadSpec()->SetQueueName(queue_name);
+ }
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeThreadChanged);
+}
+
+const char *BreakpointLocation::GetQueueName() const {
+ const ThreadSpec *thread_spec =
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ .GetThreadSpecNoCreate();
+ if (thread_spec)
+ return thread_spec->GetQueueName();
+ else
+ return nullptr;
+}
+
+bool BreakpointLocation::InvokeCallback(StoppointCallbackContext *context) {
+ if (m_options_up != nullptr && m_options_up->HasCallback())
+ return m_options_up->InvokeCallback(context, m_owner.GetID(), GetID());
+ else
+ return m_owner.InvokeCallback(context, GetID());
+}
+
+bool BreakpointLocation::IsCallbackSynchronous() {
+ if (m_options_up != nullptr && m_options_up->HasCallback())
+ return m_options_up->IsCallbackSynchronous();
+ else
+ return m_owner.GetOptions().IsCallbackSynchronous();
+}
+
+void BreakpointLocation::SetCallback(BreakpointHitCallback callback,
+ void *baton, bool is_synchronous) {
+ // The default "Baton" class will keep a copy of "baton" and won't free or
+ // delete it when it goes out of scope.
+ GetLocationOptions().SetCallback(
+ callback, std::make_shared<UntypedBaton>(baton), is_synchronous);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged);
+}
+
+void BreakpointLocation::SetCallback(BreakpointHitCallback callback,
+ const BatonSP &baton_sp,
+ bool is_synchronous) {
+ GetLocationOptions().SetCallback(callback, baton_sp, is_synchronous);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeCommandChanged);
+}
+
+void BreakpointLocation::ClearCallback() {
+ GetLocationOptions().ClearCallback();
+}
+
+void BreakpointLocation::SetCondition(const char *condition) {
+ GetLocationOptions().SetCondition(condition);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeConditionChanged);
+}
+
+const char *BreakpointLocation::GetConditionText(size_t *hash) const {
+ return GetOptionsSpecifyingKind(BreakpointOptions::eCondition)
+ .GetConditionText(hash);
+}
+
+bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
+ Status &error) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+
+ std::lock_guard<std::mutex> guard(m_condition_mutex);
+
+ size_t condition_hash;
+ const char *condition_text = GetConditionText(&condition_hash);
+
+ if (!condition_text) {
+ m_user_expression_sp.reset();
+ return false;
+ }
+
+ error.Clear();
+
+ DiagnosticManager diagnostics;
+
+ if (condition_hash != m_condition_hash || !m_user_expression_sp ||
+ !m_user_expression_sp->IsParseCacheable() ||
+ !m_user_expression_sp->MatchesContext(exe_ctx)) {
+ LanguageType language = eLanguageTypeUnknown;
+ // See if we can figure out the language from the frame, otherwise use the
+ // default language:
+ CompileUnit *comp_unit = m_address.CalculateSymbolContextCompileUnit();
+ if (comp_unit)
+ language = comp_unit->GetLanguage();
+
+ m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage(
+ condition_text, llvm::StringRef(), language, Expression::eResultTypeAny,
+ EvaluateExpressionOptions(), nullptr, error));
+ if (error.Fail()) {
+ LLDB_LOGF(log, "Error getting condition expression: %s.",
+ error.AsCString());
+ m_user_expression_sp.reset();
+ return true;
+ }
+
+ if (!m_user_expression_sp->Parse(diagnostics, exe_ctx,
+ eExecutionPolicyOnlyWhenNeeded, true,
+ false)) {
+ error.SetErrorStringWithFormat(
+ "Couldn't parse conditional expression:\n%s",
+ diagnostics.GetString().c_str());
+ m_user_expression_sp.reset();
+ return true;
+ }
+
+ m_condition_hash = condition_hash;
+ }
+
+ // We need to make sure the user sees any parse errors in their condition, so
+ // we'll hook the constructor errors up to the debugger's Async I/O.
+
+ ValueObjectSP result_value_sp;
+
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetIgnoreBreakpoints(true);
+ options.SetTryAllThreads(true);
+ options.SetSuppressPersistentResult(
+ true); // Don't generate a user variable for condition expressions.
+
+ Status expr_error;
+
+ diagnostics.Clear();
+
+ ExpressionVariableSP result_variable_sp;
+
+ ExpressionResults result_code = m_user_expression_sp->Execute(
+ diagnostics, exe_ctx, options, m_user_expression_sp, result_variable_sp);
+
+ bool ret;
+
+ if (result_code == eExpressionCompleted) {
+ if (!result_variable_sp) {
+ error.SetErrorString("Expression did not return a result");
+ return false;
+ }
+
+ result_value_sp = result_variable_sp->GetValueObject();
+
+ if (result_value_sp) {
+ ret = result_value_sp->IsLogicalTrue(error);
+ if (log) {
+ if (error.Success()) {
+ LLDB_LOGF(log, "Condition successfully evaluated, result is %s.\n",
+ ret ? "true" : "false");
+ } else {
+ error.SetErrorString(
+ "Failed to get an integer result from the expression");
+ ret = false;
+ }
+ }
+ } else {
+ ret = false;
+ error.SetErrorString("Failed to get any result from the expression");
+ }
+ } else {
+ ret = false;
+ error.SetErrorStringWithFormat("Couldn't execute expression:\n%s",
+ diagnostics.GetString().c_str());
+ }
+
+ return ret;
+}
+
+uint32_t BreakpointLocation::GetIgnoreCount() const {
+ return GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount)
+ .GetIgnoreCount();
+}
+
+void BreakpointLocation::SetIgnoreCount(uint32_t n) {
+ GetLocationOptions().SetIgnoreCount(n);
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeIgnoreChanged);
+}
+
+void BreakpointLocation::DecrementIgnoreCount() {
+ if (m_options_up != nullptr) {
+ uint32_t loc_ignore = m_options_up->GetIgnoreCount();
+ if (loc_ignore != 0)
+ m_options_up->SetIgnoreCount(loc_ignore - 1);
+ }
+}
+
+bool BreakpointLocation::IgnoreCountShouldStop() {
+ uint32_t owner_ignore = GetBreakpoint().GetIgnoreCount();
+ uint32_t loc_ignore = 0;
+ if (m_options_up != nullptr)
+ loc_ignore = m_options_up->GetIgnoreCount();
+
+ if (loc_ignore != 0 || owner_ignore != 0) {
+ m_owner.DecrementIgnoreCount();
+ DecrementIgnoreCount(); // Have to decrement our owners' ignore count,
+ // since it won't get a chance to.
+ return false;
+ }
+ return true;
+}
+
+BreakpointOptions &BreakpointLocation::GetLocationOptions() {
+ // If we make the copy we don't copy the callbacks because that is
+ // potentially expensive and we don't want to do that for the simple case
+ // where someone is just disabling the location.
+ if (m_options_up == nullptr)
+ m_options_up = std::make_unique<BreakpointOptions>(false);
+
+ return *m_options_up;
+}
+
+bool BreakpointLocation::ValidForThisThread(Thread &thread) {
+ return thread.MatchesSpec(
+ GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ .GetThreadSpecNoCreate());
+}
+
+// RETURNS - true if we should stop at this breakpoint, false if we
+// should continue. Note, we don't check the thread spec for the breakpoint
+// here, since if the breakpoint is not for this thread, then the event won't
+// even get reported, so the check is redundant.
+
+bool BreakpointLocation::ShouldStop(StoppointCallbackContext *context) {
+ bool should_stop = true;
+ Log *log = GetLog(LLDBLog::Breakpoints);
+
+ // Do this first, if a location is disabled, it shouldn't increment its hit
+ // count.
+ if (!IsEnabled())
+ return false;
+
+ // We only run synchronous callbacks in ShouldStop:
+ context->is_synchronous = true;
+ should_stop = InvokeCallback(context);
+
+ if (log) {
+ StreamString s;
+ GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ LLDB_LOGF(log, "Hit breakpoint location: %s, %s.\n", s.GetData(),
+ should_stop ? "stopping" : "continuing");
+ }
+
+ return should_stop;
+}
+
+void BreakpointLocation::BumpHitCount() {
+ if (IsEnabled()) {
+ // Step our hit count, and also step the hit count of the owner.
+ m_hit_counter.Increment();
+ m_owner.m_hit_counter.Increment();
+ }
+}
+
+void BreakpointLocation::UndoBumpHitCount() {
+ if (IsEnabled()) {
+ // Step our hit count, and also step the hit count of the owner.
+ m_hit_counter.Decrement();
+ m_owner.m_hit_counter.Decrement();
+ }
+}
+
+bool BreakpointLocation::IsResolved() const {
+ return m_bp_site_sp.get() != nullptr;
+}
+
+lldb::BreakpointSiteSP BreakpointLocation::GetBreakpointSite() const {
+ return m_bp_site_sp;
+}
+
+bool BreakpointLocation::ResolveBreakpointSite() {
+ if (m_bp_site_sp)
+ return true;
+
+ Process *process = m_owner.GetTarget().GetProcessSP().get();
+ if (process == nullptr)
+ return false;
+
+ lldb::break_id_t new_id =
+ process->CreateBreakpointSite(shared_from_this(), m_owner.IsHardware());
+
+ if (new_id == LLDB_INVALID_BREAK_ID) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+ if (log)
+ log->Warning("Failed to add breakpoint site at 0x%" PRIx64,
+ m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()));
+ }
+
+ return IsResolved();
+}
+
+bool BreakpointLocation::SetBreakpointSite(BreakpointSiteSP &bp_site_sp) {
+ m_bp_site_sp = bp_site_sp;
+ SendBreakpointLocationChangedEvent(eBreakpointEventTypeLocationsResolved);
+ return true;
+}
+
+bool BreakpointLocation::ClearBreakpointSite() {
+ if (m_bp_site_sp.get()) {
+ ProcessSP process_sp(m_owner.GetTarget().GetProcessSP());
+ // If the process exists, get it to remove the owner, it will remove the
+ // physical implementation of the breakpoint as well if there are no more
+ // owners. Otherwise just remove this owner.
+ if (process_sp)
+ process_sp->RemoveConstituentFromBreakpointSite(GetBreakpoint().GetID(),
+ GetID(), m_bp_site_sp);
+ else
+ m_bp_site_sp->RemoveConstituent(GetBreakpoint().GetID(), GetID());
+
+ m_bp_site_sp.reset();
+ return true;
+ }
+ return false;
+}
+
+void BreakpointLocation::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ SymbolContext sc;
+
+ // If the description level is "initial" then the breakpoint is printing out
+ // our initial state, and we should let it decide how it wants to print our
+ // label.
+ if (level != eDescriptionLevelInitial) {
+ s->Indent();
+ BreakpointID::GetCanonicalReference(s, m_owner.GetID(), GetID());
+ }
+
+ if (level == lldb::eDescriptionLevelBrief)
+ return;
+
+ if (level != eDescriptionLevelInitial)
+ s->PutCString(": ");
+
+ if (level == lldb::eDescriptionLevelVerbose)
+ s->IndentMore();
+
+ if (m_address.IsSectionOffset()) {
+ m_address.CalculateSymbolContext(&sc);
+
+ if (level == lldb::eDescriptionLevelFull ||
+ level == eDescriptionLevelInitial) {
+ if (IsReExported())
+ s->PutCString("re-exported target = ");
+ else
+ s->PutCString("where = ");
+ sc.DumpStopContext(s, m_owner.GetTarget().GetProcessSP().get(), m_address,
+ false, true, false, true, true, true);
+ } else {
+ if (sc.module_sp) {
+ s->EOL();
+ s->Indent("module = ");
+ sc.module_sp->GetFileSpec().Dump(s->AsRawOstream());
+ }
+
+ if (sc.comp_unit != nullptr) {
+ s->EOL();
+ s->Indent("compile unit = ");
+ sc.comp_unit->GetPrimaryFile().GetFilename().Dump(s);
+
+ if (sc.function != nullptr) {
+ s->EOL();
+ s->Indent("function = ");
+ s->PutCString(sc.function->GetName().AsCString("<unknown>"));
+ if (ConstString mangled_name =
+ sc.function->GetMangled().GetMangledName()) {
+ s->EOL();
+ s->Indent("mangled function = ");
+ s->PutCString(mangled_name.AsCString());
+ }
+ }
+
+ if (sc.line_entry.line > 0) {
+ s->EOL();
+ s->Indent("location = ");
+ sc.line_entry.DumpStopContext(s, true);
+ }
+
+ } else {
+ // If we don't have a comp unit, see if we have a symbol we can print.
+ if (sc.symbol) {
+ s->EOL();
+ if (IsReExported())
+ s->Indent("re-exported target = ");
+ else
+ s->Indent("symbol = ");
+ s->PutCString(sc.symbol->GetName().AsCString("<unknown>"));
+ }
+ }
+ }
+ }
+
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->Indent();
+ }
+
+ if (m_address.IsSectionOffset() &&
+ (level == eDescriptionLevelFull || level == eDescriptionLevelInitial))
+ s->Printf(", ");
+ s->Printf("address = ");
+
+ ExecutionContextScope *exe_scope = nullptr;
+ Target *target = &m_owner.GetTarget();
+ if (target)
+ exe_scope = target->GetProcessSP().get();
+ if (exe_scope == nullptr)
+ exe_scope = target;
+
+ if (level == eDescriptionLevelInitial)
+ m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
+ Address::DumpStyleFileAddress);
+ else
+ m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress,
+ Address::DumpStyleModuleWithFileAddress);
+
+ if (IsIndirect() && m_bp_site_sp) {
+ Address resolved_address;
+ resolved_address.SetLoadAddress(m_bp_site_sp->GetLoadAddress(), target);
+ Symbol *resolved_symbol = resolved_address.CalculateSymbolContextSymbol();
+ if (resolved_symbol) {
+ if (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)
+ s->Printf(", ");
+ else if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->Indent();
+ }
+ s->Printf("indirect target = %s",
+ resolved_symbol->GetName().GetCString());
+ }
+ }
+
+ bool is_resolved = IsResolved();
+ bool is_hardware = is_resolved && m_bp_site_sp->IsHardware();
+
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->Indent();
+ s->Printf("resolved = %s\n", is_resolved ? "true" : "false");
+ s->Indent();
+ s->Printf("hardware = %s\n", is_hardware ? "true" : "false");
+ s->Indent();
+ s->Printf("hit count = %-4u\n", GetHitCount());
+
+ if (m_options_up) {
+ s->Indent();
+ m_options_up->GetDescription(s, level);
+ s->EOL();
+ }
+ s->IndentLess();
+ } else if (level != eDescriptionLevelInitial) {
+ s->Printf(", %sresolved, %shit count = %u ", (is_resolved ? "" : "un"),
+ (is_hardware ? "hardware, " : ""), GetHitCount());
+ if (m_options_up) {
+ m_options_up->GetDescription(s, level);
+ }
+ }
+}
+
+void BreakpointLocation::Dump(Stream *s) const {
+ if (s == nullptr)
+ return;
+
+ bool is_resolved = IsResolved();
+ bool is_hardware = is_resolved && m_bp_site_sp->IsHardware();
+
+ lldb::tid_t tid = GetOptionsSpecifyingKind(BreakpointOptions::eThreadSpec)
+ .GetThreadSpecNoCreate()
+ ->GetTID();
+ s->Printf("BreakpointLocation %u: tid = %4.4" PRIx64
+ " load addr = 0x%8.8" PRIx64 " state = %s type = %s breakpoint "
+ "hit_count = %-4u ignore_count = %-4u",
+ GetID(), tid,
+ (uint64_t)m_address.GetOpcodeLoadAddress(&m_owner.GetTarget()),
+ (m_options_up ? m_options_up->IsEnabled() : m_owner.IsEnabled())
+ ? "enabled "
+ : "disabled",
+ is_hardware ? "hardware" : "software", GetHitCount(),
+ GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount)
+ .GetIgnoreCount());
+}
+
+void BreakpointLocation::SendBreakpointLocationChangedEvent(
+ lldb::BreakpointEventType eventKind) {
+ if (!m_owner.IsInternal() && m_owner.GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitBreakpointChanged)) {
+ auto data_sp = std::make_shared<Breakpoint::BreakpointEventData>(
+ eventKind, m_owner.shared_from_this());
+ data_sp->GetBreakpointLocationCollection().Add(shared_from_this());
+ m_owner.GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged,
+ data_sp);
+ }
+}
+
+void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) {
+ m_address = swap_from->m_address;
+ m_should_resolve_indirect_functions =
+ swap_from->m_should_resolve_indirect_functions;
+ m_is_reexported = swap_from->m_is_reexported;
+ m_is_indirect = swap_from->m_is_indirect;
+ m_user_expression_sp.reset();
+}
+
+void BreakpointLocation::SetThreadIDInternal(lldb::tid_t thread_id) {
+ if (thread_id != LLDB_INVALID_THREAD_ID)
+ GetLocationOptions().SetThreadID(thread_id);
+ else {
+ // If we're resetting this to an invalid thread id, then don't make an
+ // options pointer just to do that.
+ if (m_options_up != nullptr)
+ m_options_up->SetThreadID(thread_id);
+ }
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp
new file mode 100644
index 000000000000..d649e889c3f7
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationCollection.cpp
@@ -0,0 +1,189 @@
+//===-- BreakpointLocationCollection.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointLocationCollection.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointLocationCollection constructor
+BreakpointLocationCollection::BreakpointLocationCollection() = default;
+
+// Destructor
+BreakpointLocationCollection::~BreakpointLocationCollection() = default;
+
+void BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ BreakpointLocationSP old_bp_loc =
+ FindByIDPair(bp_loc->GetBreakpoint().GetID(), bp_loc->GetID());
+ if (!old_bp_loc.get())
+ m_break_loc_collection.push_back(bp_loc);
+}
+
+bool BreakpointLocationCollection::Remove(lldb::break_id_t bp_id,
+ lldb::break_id_t bp_loc_id) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::iterator pos = GetIDPairIterator(bp_id, bp_loc_id); // Predicate
+ if (pos != m_break_loc_collection.end()) {
+ m_break_loc_collection.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+class BreakpointIDPairMatches {
+public:
+ BreakpointIDPairMatches(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id)
+ : m_break_id(break_id), m_break_loc_id(break_loc_id) {}
+
+ bool operator()(const BreakpointLocationSP &bp_loc) const {
+ return m_break_id == bp_loc->GetBreakpoint().GetID() &&
+ m_break_loc_id == bp_loc->GetID();
+ }
+
+private:
+ const lldb::break_id_t m_break_id;
+ const lldb::break_id_t m_break_loc_id;
+};
+
+BreakpointLocationCollection::collection::iterator
+BreakpointLocationCollection::GetIDPairIterator(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id) {
+ return std::find_if(
+ m_break_loc_collection.begin(),
+ m_break_loc_collection.end(), // Search full range
+ BreakpointIDPairMatches(break_id, break_loc_id)); // Predicate
+}
+
+BreakpointLocationCollection::collection::const_iterator
+BreakpointLocationCollection::GetIDPairConstIterator(
+ lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const {
+ return std::find_if(
+ m_break_loc_collection.begin(),
+ m_break_loc_collection.end(), // Search full range
+ BreakpointIDPairMatches(break_id, break_loc_id)); // Predicate
+}
+
+BreakpointLocationSP
+BreakpointLocationCollection::FindByIDPair(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id) {
+ BreakpointLocationSP stop_sp;
+ collection::iterator pos = GetIDPairIterator(break_id, break_loc_id);
+ if (pos != m_break_loc_collection.end())
+ stop_sp = *pos;
+
+ return stop_sp;
+}
+
+const BreakpointLocationSP BreakpointLocationCollection::FindByIDPair(
+ lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const {
+ BreakpointLocationSP stop_sp;
+ collection::const_iterator pos =
+ GetIDPairConstIterator(break_id, break_loc_id);
+ if (pos != m_break_loc_collection.end())
+ stop_sp = *pos;
+
+ return stop_sp;
+}
+
+BreakpointLocationSP BreakpointLocationCollection::GetByIndex(size_t i) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ BreakpointLocationSP stop_sp;
+ if (i < m_break_loc_collection.size())
+ stop_sp = m_break_loc_collection[i];
+
+ return stop_sp;
+}
+
+const BreakpointLocationSP
+BreakpointLocationCollection::GetByIndex(size_t i) const {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ BreakpointLocationSP stop_sp;
+ if (i < m_break_loc_collection.size())
+ stop_sp = m_break_loc_collection[i];
+
+ return stop_sp;
+}
+
+bool BreakpointLocationCollection::ShouldStop(
+ StoppointCallbackContext *context) {
+ bool shouldStop = false;
+ size_t i = 0;
+ size_t prev_size = GetSize();
+ while (i < prev_size) {
+ // ShouldStop can remove the breakpoint from the list, or even delete
+ // it, so we should
+ BreakpointLocationSP cur_loc_sp = GetByIndex(i);
+ BreakpointSP keep_bkpt_alive_sp = cur_loc_sp->GetBreakpoint().shared_from_this();
+ if (cur_loc_sp->ShouldStop(context))
+ shouldStop = true;
+
+ if (prev_size == GetSize())
+ i++;
+ prev_size = GetSize();
+ }
+ return shouldStop;
+}
+
+bool BreakpointLocationCollection::ValidForThisThread(Thread &thread) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::iterator pos, begin = m_break_loc_collection.begin(),
+ end = m_break_loc_collection.end();
+
+ for (pos = begin; pos != end; ++pos) {
+ if ((*pos)->ValidForThisThread(thread))
+ return true;
+ }
+ return false;
+}
+
+bool BreakpointLocationCollection::IsInternal() const {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::const_iterator pos, begin = m_break_loc_collection.begin(),
+ end = m_break_loc_collection.end();
+
+ bool is_internal = true;
+
+ for (pos = begin; pos != end; ++pos) {
+ if (!(*pos)->GetBreakpoint().IsInternal()) {
+ is_internal = false;
+ break;
+ }
+ }
+ return is_internal;
+}
+
+void BreakpointLocationCollection::GetDescription(
+ Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::mutex> guard(m_collection_mutex);
+ collection::iterator pos, begin = m_break_loc_collection.begin(),
+ end = m_break_loc_collection.end();
+
+ for (pos = begin; pos != end; ++pos) {
+ if (pos != begin)
+ s->PutChar(' ');
+ (*pos)->GetDescription(s, level);
+ }
+}
+
+BreakpointLocationCollection &BreakpointLocationCollection::operator=(
+ const BreakpointLocationCollection &rhs) {
+ if (this != &rhs) {
+ std::lock(m_collection_mutex, rhs.m_collection_mutex);
+ std::lock_guard<std::mutex> lhs_guard(m_collection_mutex, std::adopt_lock);
+ std::lock_guard<std::mutex> rhs_guard(rhs.m_collection_mutex, std::adopt_lock);
+ m_break_loc_collection = rhs.m_break_loc_collection;
+ }
+ return *this;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp
new file mode 100644
index 000000000000..e0f1b9b2c808
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointLocationList.cpp
@@ -0,0 +1,317 @@
+//===-- BreakpointLocationList.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointLocationList.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/SectionLoadList.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointLocationList::BreakpointLocationList(Breakpoint &owner)
+ : m_owner(owner), m_next_id(0), m_new_location_recorder(nullptr) {}
+
+BreakpointLocationList::~BreakpointLocationList() = default;
+
+BreakpointLocationSP
+BreakpointLocationList::Create(const Address &addr,
+ bool resolve_indirect_symbols) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ // The location ID is just the size of the location list + 1
+ lldb::break_id_t bp_loc_id = ++m_next_id;
+ BreakpointLocationSP bp_loc_sp(
+ new BreakpointLocation(bp_loc_id, m_owner, addr, LLDB_INVALID_THREAD_ID,
+ m_owner.IsHardware(), resolve_indirect_symbols));
+ m_locations.push_back(bp_loc_sp);
+ m_address_to_location[addr] = bp_loc_sp;
+ return bp_loc_sp;
+}
+
+bool BreakpointLocationList::ShouldStop(StoppointCallbackContext *context,
+ lldb::break_id_t break_id) {
+ BreakpointLocationSP bp = FindByID(break_id);
+ if (bp) {
+ // Let the BreakpointLocation decide if it should stop here (could not have
+ // reached it's target hit count yet, or it could have a callback that
+ // decided it shouldn't stop (shared library loads/unloads).
+ return bp->ShouldStop(context);
+ }
+ // We should stop here since this BreakpointLocation isn't valid anymore or
+ // it doesn't exist.
+ return true;
+}
+
+lldb::break_id_t BreakpointLocationList::FindIDByAddress(const Address &addr) {
+ BreakpointLocationSP bp_loc_sp = FindByAddress(addr);
+ if (bp_loc_sp) {
+ return bp_loc_sp->GetID();
+ }
+ return LLDB_INVALID_BREAK_ID;
+}
+
+static bool Compare(BreakpointLocationSP lhs, lldb::break_id_t val) {
+ return lhs->GetID() < val;
+}
+
+BreakpointLocationSP
+BreakpointLocationList::FindByID(lldb::break_id_t break_id) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::const_iterator end = m_locations.end();
+ collection::const_iterator pos =
+ llvm::lower_bound(m_locations, break_id, Compare);
+ if (pos != end && (*pos)->GetID() == break_id)
+ return *(pos);
+ else
+ return BreakpointLocationSP();
+}
+
+size_t BreakpointLocationList::FindInModule(
+ Module *module, BreakpointLocationCollection &bp_loc_list) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ const size_t orig_size = bp_loc_list.GetSize();
+ collection::iterator pos, end = m_locations.end();
+
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ BreakpointLocationSP break_loc = (*pos);
+ SectionSP section_sp(break_loc->GetAddress().GetSection());
+ if (section_sp && section_sp->GetModule().get() == module) {
+ bp_loc_list.Add(break_loc);
+ }
+ }
+ return bp_loc_list.GetSize() - orig_size;
+}
+
+const BreakpointLocationSP
+BreakpointLocationList::FindByAddress(const Address &addr) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointLocationSP bp_loc_sp;
+ if (!m_locations.empty()) {
+ Address so_addr;
+
+ if (addr.IsSectionOffset()) {
+ so_addr = addr;
+ } else {
+ // Try and resolve as a load address if possible.
+ m_owner.GetTarget().GetSectionLoadList().ResolveLoadAddress(
+ addr.GetOffset(), so_addr);
+ if (!so_addr.IsValid()) {
+ // The address didn't resolve, so just set to passed in addr.
+ so_addr = addr;
+ }
+ }
+
+ addr_map::const_iterator pos = m_address_to_location.find(so_addr);
+ if (pos != m_address_to_location.end())
+ bp_loc_sp = pos->second;
+ }
+
+ return bp_loc_sp;
+}
+
+void BreakpointLocationList::Dump(Stream *s) const {
+ s->Printf("%p: ", static_cast<const void *>(this));
+ // s->Indent();
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ s->Printf("BreakpointLocationList with %" PRIu64 " BreakpointLocations:\n",
+ (uint64_t)m_locations.size());
+ s->IndentMore();
+ collection::const_iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos)
+ (*pos)->Dump(s);
+ s->IndentLess();
+}
+
+BreakpointLocationSP BreakpointLocationList::GetByIndex(size_t i) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointLocationSP bp_loc_sp;
+ if (i < m_locations.size())
+ bp_loc_sp = m_locations[i];
+
+ return bp_loc_sp;
+}
+
+const BreakpointLocationSP BreakpointLocationList::GetByIndex(size_t i) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ BreakpointLocationSP bp_loc_sp;
+ if (i < m_locations.size())
+ bp_loc_sp = m_locations[i];
+
+ return bp_loc_sp;
+}
+
+void BreakpointLocationList::ClearAllBreakpointSites() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos)
+ (*pos)->ClearBreakpointSite();
+}
+
+void BreakpointLocationList::ResolveAllBreakpointSites() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos, end = m_locations.end();
+
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ if ((*pos)->IsEnabled())
+ (*pos)->ResolveBreakpointSite();
+ }
+}
+
+uint32_t BreakpointLocationList::GetHitCount() const {
+ uint32_t hit_count = 0;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::const_iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos)
+ hit_count += (*pos)->GetHitCount();
+ return hit_count;
+}
+
+void BreakpointLocationList::ResetHitCount() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ for (auto &loc : m_locations)
+ loc->ResetHitCount();
+}
+
+size_t BreakpointLocationList::GetNumResolvedLocations() const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t resolve_count = 0;
+ collection::const_iterator pos, end = m_locations.end();
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ if ((*pos)->IsResolved())
+ ++resolve_count;
+ }
+ return resolve_count;
+}
+
+void BreakpointLocationList::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ collection::iterator pos, end = m_locations.end();
+
+ for (pos = m_locations.begin(); pos != end; ++pos) {
+ s->Printf(" ");
+ (*pos)->GetDescription(s, level);
+ }
+}
+
+BreakpointLocationSP BreakpointLocationList::AddLocation(
+ const Address &addr, bool resolve_indirect_symbols, bool *new_location) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ if (new_location)
+ *new_location = false;
+ BreakpointLocationSP bp_loc_sp(FindByAddress(addr));
+ if (!bp_loc_sp) {
+ bp_loc_sp = Create(addr, resolve_indirect_symbols);
+ if (bp_loc_sp) {
+ bp_loc_sp->ResolveBreakpointSite();
+
+ if (new_location)
+ *new_location = true;
+ if (m_new_location_recorder) {
+ m_new_location_recorder->Add(bp_loc_sp);
+ }
+ }
+ }
+ return bp_loc_sp;
+}
+
+void BreakpointLocationList::SwapLocation(
+ BreakpointLocationSP to_location_sp,
+ BreakpointLocationSP from_location_sp) {
+ if (!from_location_sp || !to_location_sp)
+ return;
+
+ m_address_to_location.erase(to_location_sp->GetAddress());
+ to_location_sp->SwapLocation(from_location_sp);
+ RemoveLocation(from_location_sp);
+ m_address_to_location[to_location_sp->GetAddress()] = to_location_sp;
+ to_location_sp->ResolveBreakpointSite();
+}
+
+bool BreakpointLocationList::RemoveLocation(
+ const lldb::BreakpointLocationSP &bp_loc_sp) {
+ if (bp_loc_sp) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ m_address_to_location.erase(bp_loc_sp->GetAddress());
+
+ size_t num_locations = m_locations.size();
+ for (size_t idx = 0; idx < num_locations; idx++) {
+ if (m_locations[idx].get() == bp_loc_sp.get()) {
+ RemoveLocationByIndex(idx);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void BreakpointLocationList::RemoveLocationByIndex(size_t idx) {
+ assert (idx < m_locations.size());
+ m_address_to_location.erase(m_locations[idx]->GetAddress());
+ m_locations.erase(m_locations.begin() + idx);
+}
+
+void BreakpointLocationList::RemoveInvalidLocations(const ArchSpec &arch) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ size_t idx = 0;
+ // Don't cache m_location.size() as it will change since we might remove
+ // locations from our vector...
+ while (idx < m_locations.size()) {
+ BreakpointLocation *bp_loc = m_locations[idx].get();
+ if (bp_loc->GetAddress().SectionWasDeleted()) {
+ // Section was deleted which means this breakpoint comes from a module
+ // that is no longer valid, so we should remove it.
+ RemoveLocationByIndex(idx);
+ continue;
+ }
+ if (arch.IsValid()) {
+ ModuleSP module_sp(bp_loc->GetAddress().GetModule());
+ if (module_sp) {
+ if (!arch.IsCompatibleMatch(module_sp->GetArchitecture())) {
+ // The breakpoint was in a module whose architecture is no longer
+ // compatible with "arch", so we need to remove it
+ RemoveLocationByIndex(idx);
+ continue;
+ }
+ }
+ }
+ // Only increment the index if we didn't remove the locations at index
+ // "idx"
+ ++idx;
+ }
+}
+
+void BreakpointLocationList::StartRecordingNewLocations(
+ BreakpointLocationCollection &new_locations) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ assert(m_new_location_recorder == nullptr);
+ m_new_location_recorder = &new_locations;
+}
+
+void BreakpointLocationList::StopRecordingNewLocations() {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ m_new_location_recorder = nullptr;
+}
+
+void BreakpointLocationList::Compact() {
+ lldb::break_id_t highest_id = 0;
+
+ for (BreakpointLocationSP loc_sp : m_locations) {
+ lldb::break_id_t cur_id = loc_sp->GetID();
+ if (cur_id > highest_id)
+ highest_id = cur_id;
+ }
+ m_next_id = highest_id;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp
new file mode 100644
index 000000000000..5a4aad172fc0
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointName.cpp
@@ -0,0 +1,79 @@
+//===-- BreakpointName.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/BreakpointOptions.h"
+#include "lldb/Breakpoint/BreakpointLocationCollection.h"
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const Flags::ValueType BreakpointName::Permissions::permissions_mask
+ [BreakpointName::Permissions::PermissionKinds::allPerms + 1] = {
+ (1u << 0),
+ (1u << 1),
+ (1u << 2),
+ (0x5u)
+};
+
+bool BreakpointName::Permissions::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) {
+ if (!AnySet())
+ return false;
+ s->IndentMore();
+ s->Indent();
+ if (IsSet(listPerm))
+ s->Printf("list: %s", GetAllowList() ? "allowed" : "disallowed");
+
+ if (IsSet(disablePerm))
+ s->Printf("disable: %s", GetAllowDisable() ? "allowed" : "disallowed");
+
+ if (IsSet(deletePerm))
+ s->Printf("delete: %s", GetAllowDelete() ? "allowed" : "disallowed");
+ s->IndentLess();
+ return true;
+}
+
+bool BreakpointName::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ bool printed_any = false;
+ if (!m_help.empty())
+ s->Printf("Help: %s\n", m_help.c_str());
+
+ if (GetOptions().AnySet())
+ {
+ s->PutCString("Options: \n");
+ s->IndentMore();
+ s->Indent();
+ GetOptions().GetDescription(s, level);
+ printed_any = true;
+ s->IndentLess();
+ }
+ if (GetPermissions().AnySet())
+ {
+ s->PutCString("Permissions: \n");
+ s->IndentMore();
+ s->Indent();
+ GetPermissions().GetDescription(s, level);
+ printed_any = true;
+ s->IndentLess();
+ }
+ return printed_any;
+}
+
+void BreakpointName::ConfigureBreakpoint(lldb::BreakpointSP bp_sp)
+{
+ bp_sp->GetOptions().CopyOverSetOptions(GetOptions());
+ bp_sp->GetPermissions().MergeInto(GetPermissions());
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp
new file mode 100644
index 000000000000..1db840169811
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointOptions.cpp
@@ -0,0 +1,653 @@
+//===-- BreakpointOptions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointOptions.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+const char
+ *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(
+ BreakpointOptions::CommandData::OptionNames::LastOptionName)]{
+ "UserSource", "ScriptSource", "StopOnError"};
+
+StructuredData::ObjectSP
+BreakpointOptions::CommandData::SerializeToStructuredData() {
+ size_t num_strings = user_source.GetSize();
+ if (num_strings == 0 && script_source.empty()) {
+ // We shouldn't serialize commands if there aren't any, return an empty sp
+ // to indicate this.
+ return StructuredData::ObjectSP();
+ }
+
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),
+ stop_on_error);
+
+ StructuredData::ArraySP user_source_sp(new StructuredData::Array());
+ for (size_t i = 0; i < num_strings; i++) {
+ StructuredData::StringSP item_sp(
+ new StructuredData::String(user_source[i]));
+ user_source_sp->AddItem(item_sp);
+ options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp);
+ }
+
+ options_dict_sp->AddStringItem(
+ GetKey(OptionNames::Interpreter),
+ ScriptInterpreter::LanguageToString(interpreter));
+ return options_dict_sp;
+}
+
+std::unique_ptr<BreakpointOptions::CommandData>
+BreakpointOptions::CommandData::CreateFromStructuredData(
+ const StructuredData::Dictionary &options_dict, Status &error) {
+ std::unique_ptr<CommandData> data_up(new CommandData());
+
+ bool success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::StopOnError), data_up->stop_on_error);
+
+ llvm::StringRef interpreter_str;
+ ScriptLanguage interp_language;
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::Interpreter), interpreter_str);
+
+ if (!success) {
+ error.SetErrorString("Missing command language value.");
+ return data_up;
+ }
+
+ interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);
+ if (interp_language == eScriptLanguageUnknown) {
+ error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
+ interpreter_str);
+ return data_up;
+ }
+ data_up->interpreter = interp_language;
+
+ StructuredData::Array *user_source;
+ success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
+ user_source);
+ if (success) {
+ size_t num_elems = user_source->GetSize();
+ for (size_t i = 0; i < num_elems; i++) {
+ if (std::optional<llvm::StringRef> maybe_elem_string =
+ user_source->GetItemAtIndexAsString(i))
+ data_up->user_source.AppendString(*maybe_elem_string);
+ }
+ }
+
+ return data_up;
+}
+
+const char *BreakpointOptions::g_option_names[(
+ size_t)BreakpointOptions::OptionNames::LastOptionName]{
+ "ConditionText", "IgnoreCount",
+ "EnabledState", "OneShotState", "AutoContinue"};
+
+// BreakpointOptions constructor
+BreakpointOptions::BreakpointOptions(bool all_flags_set)
+ : m_callback(nullptr), m_baton_is_command_baton(false),
+ m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false),
+ m_ignore_count(0), m_condition_text_hash(0), m_inject_condition(false),
+ m_auto_continue(false), m_set_flags(0) {
+ if (all_flags_set)
+ m_set_flags.Set(~((Flags::ValueType)0));
+}
+
+BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
+ int32_t ignore, bool one_shot,
+ bool auto_continue)
+ : m_callback(nullptr), m_baton_is_command_baton(false),
+ m_callback_is_synchronous(false), m_enabled(enabled),
+ m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text_hash(0),
+ m_inject_condition(false), m_auto_continue(auto_continue) {
+ m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue);
+ if (condition && *condition != '\0') {
+ SetCondition(condition);
+ }
+}
+
+// BreakpointOptions copy constructor
+BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
+ : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
+ m_baton_is_command_baton(rhs.m_baton_is_command_baton),
+ m_callback_is_synchronous(rhs.m_callback_is_synchronous),
+ m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
+ m_ignore_count(rhs.m_ignore_count), m_inject_condition(false),
+ m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) {
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
+ m_condition_text = rhs.m_condition_text;
+ m_condition_text_hash = rhs.m_condition_text_hash;
+}
+
+// BreakpointOptions assignment operator
+const BreakpointOptions &BreakpointOptions::
+operator=(const BreakpointOptions &rhs) {
+ m_callback = rhs.m_callback;
+ m_callback_baton_sp = rhs.m_callback_baton_sp;
+ m_baton_is_command_baton = rhs.m_baton_is_command_baton;
+ m_callback_is_synchronous = rhs.m_callback_is_synchronous;
+ m_enabled = rhs.m_enabled;
+ m_one_shot = rhs.m_one_shot;
+ m_ignore_count = rhs.m_ignore_count;
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
+ m_condition_text = rhs.m_condition_text;
+ m_condition_text_hash = rhs.m_condition_text_hash;
+ m_inject_condition = rhs.m_inject_condition;
+ m_auto_continue = rhs.m_auto_continue;
+ m_set_flags = rhs.m_set_flags;
+ return *this;
+}
+
+void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)
+{
+ if (incoming.m_set_flags.Test(eEnabled))
+ {
+ m_enabled = incoming.m_enabled;
+ m_set_flags.Set(eEnabled);
+ }
+ if (incoming.m_set_flags.Test(eOneShot))
+ {
+ m_one_shot = incoming.m_one_shot;
+ m_set_flags.Set(eOneShot);
+ }
+ if (incoming.m_set_flags.Test(eCallback))
+ {
+ m_callback = incoming.m_callback;
+ m_callback_baton_sp = incoming.m_callback_baton_sp;
+ m_callback_is_synchronous = incoming.m_callback_is_synchronous;
+ m_baton_is_command_baton = incoming.m_baton_is_command_baton;
+ m_set_flags.Set(eCallback);
+ }
+ if (incoming.m_set_flags.Test(eIgnoreCount))
+ {
+ m_ignore_count = incoming.m_ignore_count;
+ m_set_flags.Set(eIgnoreCount);
+ }
+ if (incoming.m_set_flags.Test(eCondition))
+ {
+ // If we're copying over an empty condition, mark it as unset.
+ if (incoming.m_condition_text.empty()) {
+ m_condition_text.clear();
+ m_condition_text_hash = 0;
+ m_set_flags.Clear(eCondition);
+ } else {
+ m_condition_text = incoming.m_condition_text;
+ m_condition_text_hash = incoming.m_condition_text_hash;
+ m_set_flags.Set(eCondition);
+ }
+ }
+ if (incoming.m_set_flags.Test(eAutoContinue))
+ {
+ m_auto_continue = incoming.m_auto_continue;
+ m_set_flags.Set(eAutoContinue);
+ }
+ if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) {
+ if (!m_thread_spec_up)
+ m_thread_spec_up =
+ std::make_unique<ThreadSpec>(*incoming.m_thread_spec_up);
+ else
+ *m_thread_spec_up = *incoming.m_thread_spec_up;
+ m_set_flags.Set(eThreadSpec);
+ }
+}
+
+// Destructor
+BreakpointOptions::~BreakpointOptions() = default;
+
+std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
+ Target &target, const StructuredData::Dictionary &options_dict,
+ Status &error) {
+ bool enabled = true;
+ bool one_shot = false;
+ bool auto_continue = false;
+ uint32_t ignore_count = 0;
+ llvm::StringRef condition_ref("");
+ Flags set_options;
+
+ const char *key = GetKey(OptionNames::EnabledState);
+ bool success;
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsBoolean(key, enabled);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not a boolean.", key);
+ return nullptr;
+ }
+ set_options.Set(eEnabled);
+ }
+
+ key = GetKey(OptionNames::OneShotState);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsBoolean(key, one_shot);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not a boolean.", key);
+ return nullptr;
+ }
+ set_options.Set(eOneShot);
+ }
+
+ key = GetKey(OptionNames::AutoContinue);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not a boolean.", key);
+ return nullptr;
+ }
+ set_options.Set(eAutoContinue);
+ }
+
+ key = GetKey(OptionNames::IgnoreCount);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not an integer.", key);
+ return nullptr;
+ }
+ set_options.Set(eIgnoreCount);
+ }
+
+ key = GetKey(OptionNames::ConditionText);
+ if (key && options_dict.HasKey(key)) {
+ success = options_dict.GetValueForKeyAsString(key, condition_ref);
+ if (!success) {
+ error.SetErrorStringWithFormat("%s key is not an string.", key);
+ return nullptr;
+ }
+ set_options.Set(eCondition);
+ }
+
+ std::unique_ptr<CommandData> cmd_data_up;
+ StructuredData::Dictionary *cmds_dict;
+ success = options_dict.GetValueForKeyAsDictionary(
+ CommandData::GetSerializationKey(), cmds_dict);
+ if (success && cmds_dict) {
+ Status cmds_error;
+ cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);
+ if (cmds_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Failed to deserialize breakpoint command options: %s.",
+ cmds_error.AsCString());
+ return nullptr;
+ }
+ }
+
+ auto bp_options = std::make_unique<BreakpointOptions>(
+ condition_ref.str().c_str(), enabled,
+ ignore_count, one_shot, auto_continue);
+ if (cmd_data_up) {
+ if (cmd_data_up->interpreter == eScriptLanguageNone)
+ bp_options->SetCommandDataCallback(cmd_data_up);
+ else {
+ ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter();
+ if (!interp) {
+ error.SetErrorString(
+ "Can't set script commands - no script interpreter");
+ return nullptr;
+ }
+ if (interp->GetLanguage() != cmd_data_up->interpreter) {
+ error.SetErrorStringWithFormat(
+ "Current script language doesn't match breakpoint's language: %s",
+ ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)
+ .c_str());
+ return nullptr;
+ }
+ Status script_error;
+ script_error =
+ interp->SetBreakpointCommandCallback(*bp_options, cmd_data_up);
+ if (script_error.Fail()) {
+ error.SetErrorStringWithFormat("Error generating script callback: %s.",
+ error.AsCString());
+ return nullptr;
+ }
+ }
+ }
+
+ StructuredData::Dictionary *thread_spec_dict;
+ success = options_dict.GetValueForKeyAsDictionary(
+ ThreadSpec::GetSerializationKey(), thread_spec_dict);
+ if (success) {
+ Status thread_spec_error;
+ std::unique_ptr<ThreadSpec> thread_spec_up =
+ ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
+ thread_spec_error);
+ if (thread_spec_error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Failed to deserialize breakpoint thread spec options: %s.",
+ thread_spec_error.AsCString());
+ return nullptr;
+ }
+ bp_options->SetThreadSpec(thread_spec_up);
+ }
+ return bp_options;
+}
+
+StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+ if (m_set_flags.Test(eEnabled))
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),
+ m_enabled);
+ if (m_set_flags.Test(eOneShot))
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
+ m_one_shot);
+ if (m_set_flags.Test(eAutoContinue))
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
+ m_auto_continue);
+ if (m_set_flags.Test(eIgnoreCount))
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
+ m_ignore_count);
+ if (m_set_flags.Test(eCondition))
+ options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
+ m_condition_text);
+
+ if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
+ auto cmd_baton =
+ std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
+ StructuredData::ObjectSP commands_sp =
+ cmd_baton->getItem()->SerializeToStructuredData();
+ if (commands_sp) {
+ options_dict_sp->AddItem(
+ BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
+ }
+ }
+ if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) {
+ StructuredData::ObjectSP thread_spec_sp =
+ m_thread_spec_up->SerializeToStructuredData();
+ options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);
+ }
+
+ return options_dict_sp;
+}
+
+// Callbacks
+void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
+ const lldb::BatonSP &callback_baton_sp,
+ bool callback_is_synchronous) {
+ // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but
+ // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
+ // set m_baton_is_command_baton to false, which is incorrect. One possible
+ // solution is to make the base Baton class provide a method such as:
+ // virtual StringRef getBatonId() const { return ""; }
+ // and have CommandBaton override this to return something unique, and then
+ // check for it here. Another option might be to make Baton using the llvm
+ // casting infrastructure, so that we could write something like:
+ // if (llvm::isa<CommandBaton>(callback_baton_sp))
+ // at relevant callsites instead of storing a boolean.
+ m_callback_is_synchronous = callback_is_synchronous;
+ m_callback = callback;
+ m_callback_baton_sp = callback_baton_sp;
+ m_baton_is_command_baton = false;
+ m_set_flags.Set(eCallback);
+}
+
+void BreakpointOptions::SetCallback(
+ BreakpointHitCallback callback,
+ const BreakpointOptions::CommandBatonSP &callback_baton_sp,
+ bool callback_is_synchronous) {
+ m_callback_is_synchronous = callback_is_synchronous;
+ m_callback = callback;
+ m_callback_baton_sp = callback_baton_sp;
+ m_baton_is_command_baton = true;
+ m_set_flags.Set(eCallback);
+}
+
+void BreakpointOptions::ClearCallback() {
+ m_callback = nullptr;
+ m_callback_is_synchronous = false;
+ m_callback_baton_sp.reset();
+ m_baton_is_command_baton = false;
+ m_set_flags.Clear(eCallback);
+}
+
+Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
+
+const Baton *BreakpointOptions::GetBaton() const {
+ return m_callback_baton_sp.get();
+}
+
+bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ if (m_callback) {
+ if (context->is_synchronous == IsCallbackSynchronous()) {
+ return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
+ : nullptr,
+ context, break_id, break_loc_id);
+ } else if (IsCallbackSynchronous()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool BreakpointOptions::HasCallback() const {
+ return static_cast<bool>(m_callback);
+}
+
+bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
+ if (!HasCallback())
+ return false;
+ if (!m_baton_is_command_baton)
+ return false;
+
+ auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
+ CommandData *data = cmd_baton->getItem();
+ if (!data)
+ return false;
+ command_list = data->user_source;
+ return true;
+}
+
+void BreakpointOptions::SetCondition(const char *condition) {
+ if (!condition || condition[0] == '\0') {
+ condition = "";
+ m_set_flags.Clear(eCondition);
+ }
+ else
+ m_set_flags.Set(eCondition);
+
+ m_condition_text.assign(condition);
+ std::hash<std::string> hasher;
+ m_condition_text_hash = hasher(m_condition_text);
+}
+
+const char *BreakpointOptions::GetConditionText(size_t *hash) const {
+ if (!m_condition_text.empty()) {
+ if (hash)
+ *hash = m_condition_text_hash;
+
+ return m_condition_text.c_str();
+ } else {
+ return nullptr;
+ }
+}
+
+const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
+ return m_thread_spec_up.get();
+}
+
+ThreadSpec *BreakpointOptions::GetThreadSpec() {
+ if (m_thread_spec_up == nullptr) {
+ m_set_flags.Set(eThreadSpec);
+ m_thread_spec_up = std::make_unique<ThreadSpec>();
+ }
+
+ return m_thread_spec_up.get();
+}
+
+void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
+ GetThreadSpec()->SetTID(thread_id);
+ m_set_flags.Set(eThreadSpec);
+}
+
+void BreakpointOptions::SetThreadSpec(
+ std::unique_ptr<ThreadSpec> &thread_spec_up) {
+ m_thread_spec_up = std::move(thread_spec_up);
+ m_set_flags.Set(eThreadSpec);
+}
+
+void BreakpointOptions::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) const {
+ // Figure out if there are any options not at their default value, and only
+ // print anything if there are:
+
+ if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||
+ (GetThreadSpecNoCreate() != nullptr &&
+ GetThreadSpecNoCreate()->HasSpecification())) {
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->IndentMore();
+ s->Indent();
+ s->PutCString("Breakpoint Options:\n");
+ s->IndentMore();
+ s->Indent();
+ } else
+ s->PutCString(" Options: ");
+
+ if (m_ignore_count > 0)
+ s->Printf("ignore: %d ", m_ignore_count);
+ s->Printf("%sabled ", m_enabled ? "en" : "dis");
+
+ if (m_one_shot)
+ s->Printf("one-shot ");
+
+ if (m_auto_continue)
+ s->Printf("auto-continue ");
+
+ if (m_thread_spec_up)
+ m_thread_spec_up->GetDescription(s, level);
+
+ if (level == lldb::eDescriptionLevelFull) {
+ s->IndentLess();
+ s->IndentMore();
+ }
+ }
+
+ if (m_callback_baton_sp.get()) {
+ if (level != eDescriptionLevelBrief) {
+ s->EOL();
+ m_callback_baton_sp->GetDescription(s->AsRawOstream(), level,
+ s->GetIndentLevel());
+ }
+ }
+ if (!m_condition_text.empty()) {
+ if (level != eDescriptionLevelBrief) {
+ s->EOL();
+ s->Printf("Condition: %s\n", m_condition_text.c_str());
+ }
+ }
+}
+
+void BreakpointOptions::CommandBaton::GetDescription(
+ llvm::raw_ostream &s, lldb::DescriptionLevel level,
+ unsigned indentation) const {
+ const CommandData *data = getItem();
+
+ if (level == eDescriptionLevelBrief) {
+ s << ", commands = "
+ << ((data && data->user_source.GetSize() > 0) ? "yes" : "no");
+ return;
+ }
+
+ indentation += 2;
+ s.indent(indentation);
+ s << "Breakpoint commands";
+ if (data->interpreter != eScriptLanguageNone)
+ s << llvm::formatv(" ({0}):\n",
+ ScriptInterpreter::LanguageToString(data->interpreter));
+ else
+ s << ":\n";
+
+ indentation += 2;
+ if (data && data->user_source.GetSize() > 0) {
+ for (llvm::StringRef str : data->user_source) {
+ s.indent(indentation);
+ s << str << "\n";
+ }
+ } else
+ s << "No commands.\n";
+}
+
+void BreakpointOptions::SetCommandDataCallback(
+ std::unique_ptr<CommandData> &cmd_data) {
+ cmd_data->interpreter = eScriptLanguageNone;
+ auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));
+ SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);
+ m_set_flags.Set(eCallback);
+}
+
+bool BreakpointOptions::BreakpointOptionsCallbackFunction(
+ void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id) {
+ bool ret_value = true;
+ if (baton == nullptr)
+ return true;
+
+ CommandData *data = (CommandData *)baton;
+ StringList &commands = data->user_source;
+
+ if (commands.GetSize() > 0) {
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target) {
+ Debugger &debugger = target->GetDebugger();
+ CommandReturnObject result(debugger.GetUseColor());
+
+ // Rig up the results secondary output stream to the debugger's, so the
+ // output will come out synchronously if the debugger is set up that way.
+ StreamSP output_stream(debugger.GetAsyncOutputStream());
+ StreamSP error_stream(debugger.GetAsyncErrorStream());
+ result.SetImmediateOutputStream(output_stream);
+ result.SetImmediateErrorStream(error_stream);
+
+ CommandInterpreterRunOptions options;
+ options.SetStopOnContinue(true);
+ options.SetStopOnError(data->stop_on_error);
+ options.SetEchoCommands(true);
+ options.SetPrintResults(true);
+ options.SetPrintErrors(true);
+ options.SetAddToHistory(false);
+
+ debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx,
+ options, result);
+ result.GetImmediateOutputStream()->Flush();
+ result.GetImmediateErrorStream()->Flush();
+ }
+ }
+ return ret_value;
+}
+
+void BreakpointOptions::Clear()
+{
+ m_set_flags.Clear();
+ m_thread_spec_up.release();
+ m_one_shot = false;
+ m_ignore_count = 0;
+ m_auto_continue = false;
+ m_callback = nullptr;
+ m_callback_baton_sp.reset();
+ m_baton_is_command_baton = false;
+ m_callback_is_synchronous = false;
+ m_enabled = false;
+ m_condition_text.clear();
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp
new file mode 100644
index 000000000000..24a38f0767fe
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointPrecondition.cpp
@@ -0,0 +1,26 @@
+//===-- BreakpointPrecondition.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointPrecondition.h"
+#include "lldb/Utility/Status.h"
+
+using namespace lldb_private;
+
+bool BreakpointPrecondition::EvaluatePrecondition(
+ StoppointCallbackContext &context) {
+ return false;
+}
+
+void BreakpointPrecondition::GetDescription(Stream &stream,
+ lldb::DescriptionLevel level) {}
+
+Status BreakpointPrecondition::ConfigurePrecondition(Args &args) {
+ Status error;
+ error.SetErrorString("Base breakpoint precondition has no options.");
+ return error;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp
new file mode 100644
index 000000000000..2d52cbf827ef
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolver.cpp
@@ -0,0 +1,361 @@
+//===-- BreakpointResolver.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolver.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+// Have to include the other breakpoint resolver types here so the static
+// create from StructuredData can call them.
+#include "lldb/Breakpoint/BreakpointResolverAddress.h"
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
+#include "lldb/Breakpoint/BreakpointResolverName.h"
+#include "lldb/Breakpoint/BreakpointResolverScripted.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/SearchFilter.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StreamString.h"
+#include <optional>
+
+using namespace lldb_private;
+using namespace lldb;
+
+// BreakpointResolver:
+const char *BreakpointResolver::g_ty_to_name[] = {"FileAndLine", "Address",
+ "SymbolName", "SourceRegex",
+ "Python", "Exception",
+ "Unknown"};
+
+const char *BreakpointResolver::g_option_names[static_cast<uint32_t>(
+ BreakpointResolver::OptionNames::LastOptionName)] = {
+ "AddressOffset", "Exact", "FileName", "Inlines", "Language",
+ "LineNumber", "Column", "ModuleName", "NameMask", "Offset",
+ "PythonClass", "Regex", "ScriptArgs", "SectionName", "SearchDepth",
+ "SkipPrologue", "SymbolNames"};
+
+const char *BreakpointResolver::ResolverTyToName(enum ResolverTy type) {
+ if (type > LastKnownResolverType)
+ return g_ty_to_name[UnknownResolver];
+
+ return g_ty_to_name[type];
+}
+
+BreakpointResolver::ResolverTy
+BreakpointResolver::NameToResolverTy(llvm::StringRef name) {
+ for (size_t i = 0; i < LastKnownResolverType; i++) {
+ if (name == g_ty_to_name[i])
+ return (ResolverTy)i;
+ }
+ return UnknownResolver;
+}
+
+BreakpointResolver::BreakpointResolver(const BreakpointSP &bkpt,
+ const unsigned char resolverTy,
+ lldb::addr_t offset)
+ : m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {}
+
+BreakpointResolver::~BreakpointResolver() = default;
+
+BreakpointResolverSP BreakpointResolver::CreateFromStructuredData(
+ const StructuredData::Dictionary &resolver_dict, Status &error) {
+ BreakpointResolverSP result_sp;
+ if (!resolver_dict.IsValid()) {
+ error.SetErrorString("Can't deserialize from an invalid data object.");
+ return result_sp;
+ }
+
+ llvm::StringRef subclass_name;
+
+ bool success = resolver_dict.GetValueForKeyAsString(
+ GetSerializationSubclassKey(), subclass_name);
+
+ if (!success) {
+ error.SetErrorString("Resolver data missing subclass resolver key");
+ return result_sp;
+ }
+
+ ResolverTy resolver_type = NameToResolverTy(subclass_name);
+ if (resolver_type == UnknownResolver) {
+ error.SetErrorStringWithFormatv("Unknown resolver type: {0}.",
+ subclass_name);
+ return result_sp;
+ }
+
+ StructuredData::Dictionary *subclass_options = nullptr;
+ success = resolver_dict.GetValueForKeyAsDictionary(
+ GetSerializationSubclassOptionsKey(), subclass_options);
+ if (!success || !subclass_options || !subclass_options->IsValid()) {
+ error.SetErrorString("Resolver data missing subclass options key.");
+ return result_sp;
+ }
+
+ lldb::offset_t offset;
+ success = subclass_options->GetValueForKeyAsInteger(
+ GetKey(OptionNames::Offset), offset);
+ if (!success) {
+ error.SetErrorString("Resolver data missing offset options key.");
+ return result_sp;
+ }
+
+ switch (resolver_type) {
+ case FileLineResolver:
+ result_sp = BreakpointResolverFileLine::CreateFromStructuredData(
+ *subclass_options, error);
+ break;
+ case AddressResolver:
+ result_sp = BreakpointResolverAddress::CreateFromStructuredData(
+ *subclass_options, error);
+ break;
+ case NameResolver:
+ result_sp = BreakpointResolverName::CreateFromStructuredData(
+ *subclass_options, error);
+ break;
+ case FileRegexResolver:
+ result_sp = BreakpointResolverFileRegex::CreateFromStructuredData(
+ *subclass_options, error);
+ break;
+ case PythonResolver:
+ result_sp = BreakpointResolverScripted::CreateFromStructuredData(
+ *subclass_options, error);
+ break;
+ case ExceptionResolver:
+ error.SetErrorString("Exception resolvers are hard.");
+ break;
+ default:
+ llvm_unreachable("Should never get an unresolvable resolver type.");
+ }
+
+ if (error.Fail() || !result_sp)
+ return {};
+
+ // Add on the global offset option:
+ result_sp->SetOffset(offset);
+ return result_sp;
+}
+
+StructuredData::DictionarySP BreakpointResolver::WrapOptionsDict(
+ StructuredData::DictionarySP options_dict_sp) {
+ if (!options_dict_sp || !options_dict_sp->IsValid())
+ return StructuredData::DictionarySP();
+
+ StructuredData::DictionarySP type_dict_sp(new StructuredData::Dictionary());
+ type_dict_sp->AddStringItem(GetSerializationSubclassKey(), GetResolverName());
+ type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);
+
+ // Add the m_offset to the dictionary:
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::Offset), m_offset);
+
+ return type_dict_sp;
+}
+
+void BreakpointResolver::SetBreakpoint(const BreakpointSP &bkpt) {
+ assert(bkpt);
+ m_breakpoint = bkpt;
+ NotifyBreakpointSet();
+}
+
+void BreakpointResolver::ResolveBreakpointInModules(SearchFilter &filter,
+ ModuleList &modules) {
+ filter.SearchInModuleList(*this, modules);
+}
+
+void BreakpointResolver::ResolveBreakpoint(SearchFilter &filter) {
+ filter.Search(*this);
+}
+
+namespace {
+struct SourceLoc {
+ uint32_t line = UINT32_MAX;
+ uint16_t column;
+ SourceLoc(uint32_t l, std::optional<uint16_t> c)
+ : line(l), column(c ? *c : LLDB_INVALID_COLUMN_NUMBER) {}
+ SourceLoc(const SymbolContext &sc)
+ : line(sc.line_entry.line),
+ column(sc.line_entry.column ? sc.line_entry.column
+ : LLDB_INVALID_COLUMN_NUMBER) {}
+};
+
+bool operator<(const SourceLoc lhs, const SourceLoc rhs) {
+ if (lhs.line < rhs.line)
+ return true;
+ if (lhs.line > rhs.line)
+ return false;
+ // uint32_t a_col = lhs.column ? lhs.column : LLDB_INVALID_COLUMN_NUMBER;
+ // uint32_t b_col = rhs.column ? rhs.column : LLDB_INVALID_COLUMN_NUMBER;
+ return lhs.column < rhs.column;
+}
+} // namespace
+
+void BreakpointResolver::SetSCMatchesByLine(
+ SearchFilter &filter, SymbolContextList &sc_list, bool skip_prologue,
+ llvm::StringRef log_ident, uint32_t line, std::optional<uint16_t> column) {
+ llvm::SmallVector<SymbolContext, 16> all_scs;
+
+ for (const auto &sc : sc_list) {
+ if (Language::GetGlobalLanguageProperties()
+ .GetEnableFilterForLineBreakpoints())
+ if (Language *lang = Language::FindPlugin(sc.GetLanguage());
+ lang && lang->IgnoreForLineBreakpoints(sc))
+ continue;
+ all_scs.push_back(sc);
+ }
+
+ while (all_scs.size()) {
+ uint32_t closest_line = UINT32_MAX;
+
+ // Move all the elements with a matching file spec to the end.
+ auto &match = all_scs[0];
+ auto worklist_begin = std::partition(
+ all_scs.begin(), all_scs.end(), [&](const SymbolContext &sc) {
+ if (sc.line_entry.GetFile() == match.line_entry.GetFile() ||
+ sc.line_entry.original_file_sp->Equal(
+ *match.line_entry.original_file_sp,
+ SupportFile::eEqualFileSpecAndChecksumIfSet)) {
+ // When a match is found, keep track of the smallest line number.
+ closest_line = std::min(closest_line, sc.line_entry.line);
+ return false;
+ }
+ return true;
+ });
+
+ // (worklist_begin, worklist_end) now contains all entries for one filespec.
+ auto worklist_end = all_scs.end();
+
+ if (column) {
+ // If a column was requested, do a more precise match and only
+ // return the first location that comes before or at the
+ // requested location.
+ SourceLoc requested(line, *column);
+ // First, filter out all entries left of the requested column.
+ worklist_end = std::remove_if(
+ worklist_begin, worklist_end,
+ [&](const SymbolContext &sc) { return requested < SourceLoc(sc); });
+ // Sort the remaining entries by (line, column).
+ llvm::sort(worklist_begin, worklist_end,
+ [](const SymbolContext &a, const SymbolContext &b) {
+ return SourceLoc(a) < SourceLoc(b);
+ });
+
+ // Filter out all locations with a source location after the closest match.
+ if (worklist_begin != worklist_end)
+ worklist_end = std::remove_if(
+ worklist_begin, worklist_end, [&](const SymbolContext &sc) {
+ return SourceLoc(*worklist_begin) < SourceLoc(sc);
+ });
+ } else {
+ // Remove all entries with a larger line number.
+ // ResolveSymbolContext will always return a number that is >=
+ // the line number you pass in. So the smaller line number is
+ // always better.
+ worklist_end = std::remove_if(worklist_begin, worklist_end,
+ [&](const SymbolContext &sc) {
+ return closest_line != sc.line_entry.line;
+ });
+ }
+
+ // Sort by file address.
+ llvm::sort(worklist_begin, worklist_end,
+ [](const SymbolContext &a, const SymbolContext &b) {
+ return a.line_entry.range.GetBaseAddress().GetFileAddress() <
+ b.line_entry.range.GetBaseAddress().GetFileAddress();
+ });
+
+ // Go through and see if there are line table entries that are
+ // contiguous, and if so keep only the first of the contiguous range.
+ // We do this by picking the first location in each lexical block.
+ llvm::SmallDenseSet<Block *, 8> blocks_with_breakpoints;
+ for (auto first = worklist_begin; first != worklist_end; ++first) {
+ assert(!blocks_with_breakpoints.count(first->block));
+ blocks_with_breakpoints.insert(first->block);
+ worklist_end =
+ std::remove_if(std::next(first), worklist_end,
+ [&](const SymbolContext &sc) {
+ return blocks_with_breakpoints.count(sc.block);
+ });
+ }
+
+ // Make breakpoints out of the closest line number match.
+ for (auto &sc : llvm::make_range(worklist_begin, worklist_end))
+ AddLocation(filter, sc, skip_prologue, log_ident);
+
+ // Remove all contexts processed by this iteration.
+ all_scs.erase(worklist_begin, all_scs.end());
+ }
+}
+
+void BreakpointResolver::AddLocation(SearchFilter &filter,
+ const SymbolContext &sc,
+ bool skip_prologue,
+ llvm::StringRef log_ident) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+ Address line_start = sc.line_entry.range.GetBaseAddress();
+ if (!line_start.IsValid()) {
+ LLDB_LOGF(log,
+ "error: Unable to set breakpoint %s at file address "
+ "0x%" PRIx64 "\n",
+ log_ident.str().c_str(), line_start.GetFileAddress());
+ return;
+ }
+
+ if (!filter.AddressPasses(line_start)) {
+ LLDB_LOGF(log,
+ "Breakpoint %s at file address 0x%" PRIx64
+ " didn't pass the filter.\n",
+ log_ident.str().c_str(), line_start.GetFileAddress());
+ }
+
+ // If the line number is before the prologue end, move it there...
+ bool skipped_prologue = false;
+ if (skip_prologue && sc.function) {
+ Address prologue_addr(sc.function->GetAddressRange().GetBaseAddress());
+ if (prologue_addr.IsValid() && (line_start == prologue_addr)) {
+ const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();
+ if (prologue_byte_size) {
+ prologue_addr.Slide(prologue_byte_size);
+
+ if (filter.AddressPasses(prologue_addr)) {
+ skipped_prologue = true;
+ line_start = prologue_addr;
+ }
+ }
+ }
+ }
+
+ BreakpointLocationSP bp_loc_sp(AddLocation(line_start));
+ if (log && bp_loc_sp && !GetBreakpoint()->IsInternal()) {
+ StreamString s;
+ bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ LLDB_LOGF(log, "Added location (skipped prologue: %s): %s \n",
+ skipped_prologue ? "yes" : "no", s.GetData());
+ }
+}
+
+BreakpointLocationSP BreakpointResolver::AddLocation(Address loc_addr,
+ bool *new_location) {
+ loc_addr.Slide(m_offset);
+ return GetBreakpoint()->AddLocation(loc_addr, new_location);
+}
+
+void BreakpointResolver::SetOffset(lldb::addr_t offset) {
+ // There may already be an offset, so we are actually adjusting location
+ // addresses by the difference.
+ // lldb::addr_t slide = offset - m_offset;
+ // FIXME: We should go fix up all the already set locations for the new
+ // slide.
+
+ m_offset = offset;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp
new file mode 100644
index 000000000000..ee4cbd50f9ee
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverAddress.cpp
@@ -0,0 +1,176 @@
+//===-- BreakpointResolverAddress.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverAddress.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverAddress:
+BreakpointResolverAddress::BreakpointResolverAddress(
+ const BreakpointSP &bkpt, const Address &addr, const FileSpec &module_spec)
+ : BreakpointResolver(bkpt, BreakpointResolver::AddressResolver),
+ m_addr(addr), m_resolved_addr(LLDB_INVALID_ADDRESS),
+ m_module_filespec(module_spec) {}
+
+BreakpointResolverAddress::BreakpointResolverAddress(const BreakpointSP &bkpt,
+ const Address &addr)
+ : BreakpointResolver(bkpt, BreakpointResolver::AddressResolver),
+ m_addr(addr), m_resolved_addr(LLDB_INVALID_ADDRESS) {}
+
+BreakpointResolverSP BreakpointResolverAddress::CreateFromStructuredData(
+ const StructuredData::Dictionary &options_dict, Status &error) {
+ llvm::StringRef module_name;
+ lldb::offset_t addr_offset;
+ FileSpec module_filespec;
+ bool success;
+
+ success = options_dict.GetValueForKeyAsInteger(
+ GetKey(OptionNames::AddressOffset), addr_offset);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find address offset entry.");
+ return nullptr;
+ }
+ Address address(addr_offset);
+
+ success = options_dict.HasKey(GetKey(OptionNames::ModuleName));
+ if (success) {
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::ModuleName), module_name);
+ if (!success) {
+ error.SetErrorString("BRA::CFSD: Couldn't read module name entry.");
+ return nullptr;
+ }
+ module_filespec.SetFile(module_name, FileSpec::Style::native);
+ }
+ return std::make_shared<BreakpointResolverAddress>(nullptr, address,
+ module_filespec);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverAddress::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+ SectionSP section_sp = m_addr.GetSection();
+ if (section_sp) {
+ if (ModuleSP module_sp = section_sp->GetModule()) {
+ const FileSpec &module_fspec = module_sp->GetFileSpec();
+ options_dict_sp->AddStringItem(GetKey(OptionNames::ModuleName),
+ module_fspec.GetPath().c_str());
+ }
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::AddressOffset),
+ m_addr.GetOffset());
+ } else {
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::AddressOffset),
+ m_addr.GetOffset());
+ if (m_module_filespec) {
+ options_dict_sp->AddStringItem(GetKey(OptionNames::ModuleName),
+ m_module_filespec.GetPath());
+ }
+ }
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+void BreakpointResolverAddress::ResolveBreakpoint(SearchFilter &filter) {
+ // If the address is not section relative, then we should not try to re-
+ // resolve it, it is just some random address and we wouldn't know what to do
+ // on reload. But if it is section relative, we need to re-resolve it since
+ // the section it's in may have shifted on re-run.
+ bool re_resolve = false;
+ if (m_addr.GetSection() || m_module_filespec)
+ re_resolve = true;
+ else if (GetBreakpoint()->GetNumLocations() == 0)
+ re_resolve = true;
+
+ if (re_resolve)
+ BreakpointResolver::ResolveBreakpoint(filter);
+}
+
+void BreakpointResolverAddress::ResolveBreakpointInModules(
+ SearchFilter &filter, ModuleList &modules) {
+ // See comment in ResolveBreakpoint.
+ bool re_resolve = false;
+ if (m_addr.GetSection())
+ re_resolve = true;
+ else if (GetBreakpoint()->GetNumLocations() == 0)
+ re_resolve = true;
+
+ if (re_resolve)
+ BreakpointResolver::ResolveBreakpointInModules(filter, modules);
+}
+
+Searcher::CallbackReturn BreakpointResolverAddress::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr) {
+ BreakpointSP breakpoint_sp = GetBreakpoint();
+ Breakpoint &breakpoint = *breakpoint_sp;
+
+ if (filter.AddressPasses(m_addr)) {
+ if (breakpoint.GetNumLocations() == 0) {
+ // If the address is just an offset, and we're given a module, see if we
+ // can find the appropriate module loaded in the binary, and fix up
+ // m_addr to use that.
+ if (!m_addr.IsSectionOffset() && m_module_filespec) {
+ Target &target = breakpoint.GetTarget();
+ ModuleSpec module_spec(m_module_filespec);
+ ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec);
+ if (module_sp) {
+ Address tmp_address;
+ if (module_sp->ResolveFileAddress(m_addr.GetOffset(), tmp_address))
+ m_addr = tmp_address;
+ }
+ }
+
+ m_resolved_addr = m_addr.GetLoadAddress(&breakpoint.GetTarget());
+ BreakpointLocationSP bp_loc_sp(AddLocation(m_addr));
+ if (bp_loc_sp && !breakpoint.IsInternal()) {
+ StreamString s;
+ bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ Log *log = GetLog(LLDBLog::Breakpoints);
+ LLDB_LOGF(log, "Added location: %s\n", s.GetData());
+ }
+ } else {
+ BreakpointLocationSP loc_sp = breakpoint.GetLocationAtIndex(0);
+ lldb::addr_t cur_load_location =
+ m_addr.GetLoadAddress(&breakpoint.GetTarget());
+ if (cur_load_location != m_resolved_addr) {
+ m_resolved_addr = cur_load_location;
+ loc_sp->ClearBreakpointSite();
+ loc_sp->ResolveBreakpointSite();
+ }
+ }
+ }
+ return Searcher::eCallbackReturnStop;
+}
+
+lldb::SearchDepth BreakpointResolverAddress::GetDepth() {
+ return lldb::eSearchDepthTarget;
+}
+
+void BreakpointResolverAddress::GetDescription(Stream *s) {
+ s->PutCString("address = ");
+ m_addr.Dump(s, GetBreakpoint()->GetTarget().GetProcessSP().get(),
+ Address::DumpStyleModuleWithFileAddress,
+ Address::DumpStyleLoadAddress);
+}
+
+void BreakpointResolverAddress::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverAddress::CopyForBreakpoint(BreakpointSP &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(
+ new BreakpointResolverAddress(breakpoint, m_addr));
+ return ret_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
new file mode 100644
index 000000000000..16c4ee1b88d1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp
@@ -0,0 +1,340 @@
+//===-- BreakpointResolverFileLine.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+#include <optional>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverFileLine:
+BreakpointResolverFileLine::BreakpointResolverFileLine(
+ const BreakpointSP &bkpt, lldb::addr_t offset, bool skip_prologue,
+ const SourceLocationSpec &location_spec,
+ std::optional<llvm::StringRef> removed_prefix_opt)
+ : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),
+ m_location_spec(location_spec), m_skip_prologue(skip_prologue),
+ m_removed_prefix_opt(removed_prefix_opt) {}
+
+BreakpointResolverSP BreakpointResolverFileLine::CreateFromStructuredData(
+ const StructuredData::Dictionary &options_dict, Status &error) {
+ llvm::StringRef filename;
+ uint32_t line;
+ uint16_t column;
+ bool check_inlines;
+ bool skip_prologue;
+ bool exact_match;
+ bool success;
+
+ lldb::addr_t offset = 0;
+
+ success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),
+ filename);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find filename entry.");
+ return nullptr;
+ }
+
+ success = options_dict.GetValueForKeyAsInteger(
+ GetKey(OptionNames::LineNumber), line);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find line number entry.");
+ return nullptr;
+ }
+
+ success =
+ options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);
+ if (!success) {
+ // Backwards compatibility.
+ column = 0;
+ }
+
+ success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),
+ check_inlines);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry.");
+ return nullptr;
+ }
+
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::SkipPrologue), skip_prologue);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry.");
+ return nullptr;
+ }
+
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::ExactMatch), exact_match);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
+ return nullptr;
+ }
+
+ SourceLocationSpec location_spec(FileSpec(filename), line, column,
+ check_inlines, exact_match);
+ if (!location_spec)
+ return nullptr;
+
+ return std::make_shared<BreakpointResolverFileLine>(
+ nullptr, offset, skip_prologue, location_spec);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverFileLine::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
+ m_skip_prologue);
+ options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),
+ m_location_spec.GetFileSpec().GetPath());
+ options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),
+ m_location_spec.GetLine().value_or(0));
+ options_dict_sp->AddIntegerItem(
+ GetKey(OptionNames::Column),
+ m_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER));
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines),
+ m_location_spec.GetCheckInlines());
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
+ m_location_spec.GetExactMatch());
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+// Filter the symbol context list to remove contexts where the line number was
+// moved into a new function. We do this conservatively, so if e.g. we cannot
+// resolve the function in the context (which can happen in case of line-table-
+// only debug info), we leave the context as is. The trickiest part here is
+// handling inlined functions -- in this case we need to make sure we look at
+// the declaration line of the inlined function, NOT the function it was
+// inlined into.
+void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) {
+ if (m_location_spec.GetExactMatch())
+ return; // Nothing to do. Contexts are precise.
+
+ Log *log = GetLog(LLDBLog::Breakpoints);
+ for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {
+ SymbolContext sc;
+ sc_list.GetContextAtIndex(i, sc);
+ if (!sc.block)
+ continue;
+
+ FileSpec file;
+ uint32_t line;
+ const Block *inline_block = sc.block->GetContainingInlinedBlock();
+ if (inline_block) {
+ const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();
+ if (!inline_declaration.IsValid())
+ continue;
+ file = inline_declaration.GetFile();
+ line = inline_declaration.GetLine();
+ } else if (sc.function)
+ sc.function->GetStartLineSourceInfo(file, line);
+ else
+ continue;
+
+ if (file != sc.line_entry.GetFile()) {
+ LLDB_LOG(log, "unexpected symbol context file {0}",
+ sc.line_entry.GetFile());
+ continue;
+ }
+
+ // Compare the requested line number with the line of the function
+ // declaration. In case of a function declared as:
+ //
+ // int
+ // foo()
+ // {
+ // ...
+ //
+ // the compiler will set the declaration line to the "foo" line, which is
+ // the reason why we have -1 here. This can fail in case of two inline
+ // functions defined back-to-back:
+ //
+ // inline int foo1() { ... }
+ // inline int foo2() { ... }
+ //
+ // but that's the best we can do for now.
+ // One complication, if the line number returned from GetStartLineSourceInfo
+ // is 0, then we can't do this calculation. That can happen if
+ // GetStartLineSourceInfo gets an error, or if the first line number in
+ // the function really is 0 - which happens for some languages.
+
+ // But only do this calculation if the line number we found in the SC
+ // was different from the one requested in the source file. If we actually
+ // found an exact match it must be valid.
+
+ if (m_location_spec.GetLine() == sc.line_entry.line)
+ continue;
+
+ const int decl_line_is_too_late_fudge = 1;
+ if (line &&
+ m_location_spec.GetLine() < line - decl_line_is_too_late_fudge) {
+ LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line);
+ sc_list.RemoveContextAtIndex(i);
+ --i;
+ }
+ }
+}
+
+void BreakpointResolverFileLine::DeduceSourceMapping(
+ const SymbolContextList &sc_list) {
+ Target &target = GetBreakpoint()->GetTarget();
+ if (!target.GetAutoSourceMapRelative())
+ return;
+
+ Log *log = GetLog(LLDBLog::Breakpoints);
+ // Check if "b" is a suffix of "a".
+ // And return std::nullopt if not or the new path
+ // of "a" after consuming "b" from the back.
+ auto check_suffix =
+ [](llvm::StringRef a, llvm::StringRef b,
+ bool case_sensitive) -> std::optional<llvm::StringRef> {
+ if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) {
+ // Note sc_file_dir and request_file_dir below are normalized
+ // and always contain the path separator '/'.
+ if (a.empty() || a.ends_with("/")) {
+ return a;
+ }
+ }
+ return std::nullopt;
+ };
+
+ FileSpec request_file = m_location_spec.GetFileSpec();
+
+ // Only auto deduce source map if breakpoint is full path.
+ // Note: an existing source map reverse mapping (m_removed_prefix_opt has
+ // value) may make request_file relative.
+ if (!m_removed_prefix_opt.has_value() && request_file.IsRelative())
+ return;
+
+ const bool case_sensitive = request_file.IsCaseSensitive();
+ for (const SymbolContext &sc : sc_list) {
+ FileSpec sc_file = sc.line_entry.GetFile();
+
+ if (FileSpec::Equal(sc_file, request_file, /*full*/ true))
+ continue;
+
+ llvm::StringRef sc_file_dir = sc_file.GetDirectory().GetStringRef();
+ llvm::StringRef request_file_dir =
+ request_file.GetDirectory().GetStringRef();
+
+ llvm::StringRef new_mapping_from;
+ llvm::SmallString<256> new_mapping_to;
+
+ // Adding back any potentially reverse mapping stripped prefix.
+ // for new_mapping_to.
+ if (m_removed_prefix_opt.has_value())
+ llvm::sys::path::append(new_mapping_to, *m_removed_prefix_opt);
+
+ std::optional<llvm::StringRef> new_mapping_from_opt =
+ check_suffix(sc_file_dir, request_file_dir, case_sensitive);
+ if (new_mapping_from_opt) {
+ new_mapping_from = *new_mapping_from_opt;
+ if (new_mapping_to.empty())
+ new_mapping_to = ".";
+ } else {
+ std::optional<llvm::StringRef> new_mapping_to_opt =
+ check_suffix(request_file_dir, sc_file_dir, case_sensitive);
+ if (new_mapping_to_opt) {
+ new_mapping_from = ".";
+ llvm::sys::path::append(new_mapping_to, *new_mapping_to_opt);
+ }
+ }
+
+ if (!new_mapping_from.empty() && !new_mapping_to.empty()) {
+ LLDB_LOG(log, "generating auto source map from {0} to {1}",
+ new_mapping_from, new_mapping_to);
+ if (target.GetSourcePathMap().AppendUnique(new_mapping_from,
+ new_mapping_to,
+ /*notify*/ true))
+ target.GetStatistics().IncreaseSourceMapDeduceCount();
+ }
+ }
+}
+
+Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr) {
+ SymbolContextList sc_list;
+
+ // There is a tricky bit here. You can have two compilation units that
+ // #include the same file, and in one of them the function at m_line_number
+ // is used (and so code and a line entry for it is generated) but in the
+ // other it isn't. If we considered the CU's independently, then in the
+ // second inclusion, we'd move the breakpoint to the next function that
+ // actually generated code in the header file. That would end up being
+ // confusing. So instead, we do the CU iterations by hand here, then scan
+ // through the complete list of matches, and figure out the closest line
+ // number match, and only set breakpoints on that match.
+
+ // Note also that if file_spec only had a file name and not a directory,
+ // there may be many different file spec's in the resultant list. The
+ // closest line match for one will not be right for some totally different
+ // file. So we go through the match list and pull out the sets that have the
+ // same file spec in their line_entry and treat each set separately.
+
+ const uint32_t line = m_location_spec.GetLine().value_or(0);
+ const std::optional<uint16_t> column = m_location_spec.GetColumn();
+
+ const size_t num_comp_units = context.module_sp->GetNumCompileUnits();
+ for (size_t i = 0; i < num_comp_units; i++) {
+ CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));
+ if (cu_sp) {
+ if (filter.CompUnitPasses(*cu_sp))
+ cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything,
+ sc_list);
+ }
+ }
+
+ FilterContexts(sc_list);
+
+ DeduceSourceMapping(sc_list);
+
+ StreamString s;
+ s.Printf("for %s:%d ",
+ m_location_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"),
+ line);
+
+ SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), line,
+ column);
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+void BreakpointResolverFileLine::GetDescription(Stream *s) {
+ s->Printf("file = '%s', line = %u, ",
+ m_location_spec.GetFileSpec().GetPath().c_str(),
+ m_location_spec.GetLine().value_or(0));
+ auto column = m_location_spec.GetColumn();
+ if (column)
+ s->Printf("column = %u, ", *column);
+ s->Printf("exact_match = %d", m_location_spec.GetExactMatch());
+}
+
+void BreakpointResolverFileLine::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverFileLine::CopyForBreakpoint(BreakpointSP &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(
+ breakpoint, GetOffset(), m_skip_prologue, m_location_spec));
+
+ return ret_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp
new file mode 100644
index 000000000000..06d346afb9f6
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp
@@ -0,0 +1,168 @@
+//===-- BreakpointResolverFileRegex.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverFileRegex:
+BreakpointResolverFileRegex::BreakpointResolverFileRegex(
+ const lldb::BreakpointSP &bkpt, RegularExpression regex,
+ const std::unordered_set<std::string> &func_names, bool exact_match)
+ : BreakpointResolver(bkpt, BreakpointResolver::FileRegexResolver),
+ m_regex(std::move(regex)), m_exact_match(exact_match),
+ m_function_names(func_names) {}
+
+BreakpointResolverSP BreakpointResolverFileRegex::CreateFromStructuredData(
+ const StructuredData::Dictionary &options_dict, Status &error) {
+ bool success;
+
+ llvm::StringRef regex_string;
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::RegexString), regex_string);
+ if (!success) {
+ error.SetErrorString("BRFR::CFSD: Couldn't find regex entry.");
+ return nullptr;
+ }
+ RegularExpression regex(regex_string);
+
+ bool exact_match;
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::ExactMatch), exact_match);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
+ return nullptr;
+ }
+
+ // The names array is optional:
+ std::unordered_set<std::string> names_set;
+ StructuredData::Array *names_array;
+ success = options_dict.GetValueForKeyAsArray(
+ GetKey(OptionNames::SymbolNameArray), names_array);
+ if (success && names_array) {
+ 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) {
+ error.SetErrorStringWithFormat(
+ "BRFR::CFSD: Malformed element %zu in the names array.", i);
+ return nullptr;
+ }
+ names_set.insert(std::string(*maybe_name));
+ }
+ }
+
+ return std::make_shared<BreakpointResolverFileRegex>(
+ nullptr, std::move(regex), names_set, exact_match);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverFileRegex::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ options_dict_sp->AddStringItem(GetKey(OptionNames::RegexString),
+ m_regex.GetText());
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
+ m_exact_match);
+ if (!m_function_names.empty()) {
+ StructuredData::ArraySP names_array_sp(new StructuredData::Array());
+ for (std::string name : m_function_names) {
+ StructuredData::StringSP item(new StructuredData::String(name));
+ names_array_sp->AddItem(item);
+ }
+ options_dict_sp->AddItem(GetKey(OptionNames::LineNumber), names_array_sp);
+ }
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+Searcher::CallbackReturn BreakpointResolverFileRegex::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr) {
+
+ if (!context.target_sp)
+ return eCallbackReturnContinue;
+
+ CompileUnit *cu = context.comp_unit;
+ FileSpec cu_file_spec = cu->GetPrimaryFile();
+ std::vector<uint32_t> line_matches;
+ context.target_sp->GetSourceManager().FindLinesMatchingRegex(
+ cu_file_spec, m_regex, 1, UINT32_MAX, line_matches);
+
+ uint32_t num_matches = line_matches.size();
+ for (uint32_t i = 0; i < num_matches; i++) {
+ SymbolContextList sc_list;
+ // TODO: Handle SourceLocationSpec column information
+ SourceLocationSpec location_spec(cu_file_spec, line_matches[i],
+ /*column=*/std::nullopt,
+ /*check_inlines=*/false, m_exact_match);
+ cu->ResolveSymbolContext(location_spec, eSymbolContextEverything, sc_list);
+ // Find all the function names:
+ if (!m_function_names.empty()) {
+ std::vector<size_t> sc_to_remove;
+ for (size_t i = 0; i < sc_list.GetSize(); i++) {
+ SymbolContext sc_ctx;
+ sc_list.GetContextAtIndex(i, sc_ctx);
+ std::string name(
+ sc_ctx
+ .GetFunctionName(
+ Mangled::NamePreference::ePreferDemangledWithoutArguments)
+ .AsCString());
+ if (!m_function_names.count(name)) {
+ sc_to_remove.push_back(i);
+ }
+ }
+
+ if (!sc_to_remove.empty()) {
+ std::vector<size_t>::reverse_iterator iter;
+ std::vector<size_t>::reverse_iterator rend = sc_to_remove.rend();
+ for (iter = sc_to_remove.rbegin(); iter != rend; iter++) {
+ sc_list.RemoveContextAtIndex(*iter);
+ }
+ }
+ }
+
+ const bool skip_prologue = true;
+
+ BreakpointResolver::SetSCMatchesByLine(filter, sc_list, skip_prologue,
+ m_regex.GetText());
+ }
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth BreakpointResolverFileRegex::GetDepth() {
+ return lldb::eSearchDepthCompUnit;
+}
+
+void BreakpointResolverFileRegex::GetDescription(Stream *s) {
+ s->Printf("source regex = \"%s\", exact_match = %d",
+ m_regex.GetText().str().c_str(), m_exact_match);
+}
+
+void BreakpointResolverFileRegex::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverFileRegex::CopyForBreakpoint(BreakpointSP &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileRegex(
+ breakpoint, m_regex, m_function_names, m_exact_match));
+ return ret_sp;
+}
+
+void BreakpointResolverFileRegex::AddFunctionName(const char *func_name) {
+ m_function_names.insert(func_name);
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp
new file mode 100644
index 000000000000..aa86d2a26d11
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverName.cpp
@@ -0,0 +1,423 @@
+//===-- BreakpointResolverName.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverName.h"
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Architecture.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/Language.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt,
+ const char *name_cstr, FunctionNameType name_type_mask,
+ LanguageType language, Breakpoint::MatchType type, lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_match_type(type), m_language(language), m_skip_prologue(skip_prologue) {
+ if (m_match_type == Breakpoint::Regexp) {
+ m_regex = RegularExpression(name_cstr);
+ if (!m_regex.IsValid()) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+
+ if (log)
+ log->Warning("function name regexp: \"%s\" did not compile.",
+ name_cstr);
+ }
+ } else {
+ AddNameLookup(ConstString(name_cstr), name_type_mask);
+ }
+}
+
+BreakpointResolverName::BreakpointResolverName(
+ const BreakpointSP &bkpt, const char *names[], size_t num_names,
+ FunctionNameType name_type_mask, LanguageType language, lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_match_type(Breakpoint::Exact), m_language(language),
+ m_skip_prologue(skip_prologue) {
+ for (size_t i = 0; i < num_names; i++) {
+ AddNameLookup(ConstString(names[i]), name_type_mask);
+ }
+}
+
+BreakpointResolverName::BreakpointResolverName(
+ const BreakpointSP &bkpt, const std::vector<std::string> &names,
+ FunctionNameType name_type_mask, LanguageType language, lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_match_type(Breakpoint::Exact), m_language(language),
+ m_skip_prologue(skip_prologue) {
+ for (const std::string &name : names) {
+ AddNameLookup(ConstString(name.c_str(), name.size()), name_type_mask);
+ }
+}
+
+BreakpointResolverName::BreakpointResolverName(const BreakpointSP &bkpt,
+ RegularExpression func_regex,
+ lldb::LanguageType language,
+ lldb::addr_t offset,
+ bool skip_prologue)
+ : BreakpointResolver(bkpt, BreakpointResolver::NameResolver, offset),
+ m_class_name(nullptr), m_regex(std::move(func_regex)),
+ m_match_type(Breakpoint::Regexp), m_language(language),
+ m_skip_prologue(skip_prologue) {}
+
+BreakpointResolverName::BreakpointResolverName(
+ const BreakpointResolverName &rhs)
+ : BreakpointResolver(rhs.GetBreakpoint(), BreakpointResolver::NameResolver,
+ rhs.GetOffset()),
+ m_lookups(rhs.m_lookups), m_class_name(rhs.m_class_name),
+ m_regex(rhs.m_regex), m_match_type(rhs.m_match_type),
+ m_language(rhs.m_language), m_skip_prologue(rhs.m_skip_prologue) {}
+
+BreakpointResolverSP BreakpointResolverName::CreateFromStructuredData(
+ const StructuredData::Dictionary &options_dict, Status &error) {
+ LanguageType language = eLanguageTypeUnknown;
+ llvm::StringRef language_name;
+ bool success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::LanguageName), language_name);
+ if (success) {
+ language = Language::GetLanguageTypeFromString(language_name);
+ if (language == eLanguageTypeUnknown) {
+ error.SetErrorStringWithFormatv("BRN::CFSD: Unknown language: {0}.",
+ language_name);
+ return nullptr;
+ }
+ }
+
+ lldb::offset_t offset = 0;
+ success =
+ options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Offset), offset);
+ if (!success) {
+ error.SetErrorString("BRN::CFSD: Missing offset entry.");
+ return nullptr;
+ }
+
+ bool skip_prologue;
+ success = options_dict.GetValueForKeyAsBoolean(
+ GetKey(OptionNames::SkipPrologue), skip_prologue);
+ if (!success) {
+ error.SetErrorString("BRN::CFSD: Missing Skip prologue entry.");
+ return nullptr;
+ }
+
+ llvm::StringRef regex_text;
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::RegexString), regex_text);
+ if (success) {
+ return std::make_shared<BreakpointResolverName>(
+ nullptr, RegularExpression(regex_text), language, offset,
+ skip_prologue);
+ } else {
+ StructuredData::Array *names_array;
+ success = options_dict.GetValueForKeyAsArray(
+ GetKey(OptionNames::SymbolNameArray), names_array);
+ if (!success) {
+ error.SetErrorString("BRN::CFSD: Missing symbol names entry.");
+ return nullptr;
+ }
+ StructuredData::Array *names_mask_array;
+ success = options_dict.GetValueForKeyAsArray(
+ GetKey(OptionNames::NameMaskArray), names_mask_array);
+ if (!success) {
+ error.SetErrorString("BRN::CFSD: Missing symbol names mask entry.");
+ return nullptr;
+ }
+
+ size_t num_elem = names_array->GetSize();
+ if (num_elem != names_mask_array->GetSize()) {
+ error.SetErrorString(
+ "BRN::CFSD: names and names mask arrays have different sizes.");
+ return nullptr;
+ }
+
+ if (num_elem == 0) {
+ error.SetErrorString(
+ "BRN::CFSD: no name entry in a breakpoint by name breakpoint.");
+ return nullptr;
+ }
+ std::vector<std::string> names;
+ std::vector<FunctionNameType> name_masks;
+ for (size_t i = 0; i < num_elem; i++) {
+ std::optional<llvm::StringRef> maybe_name =
+ names_array->GetItemAtIndexAsString(i);
+ if (!maybe_name) {
+ error.SetErrorString("BRN::CFSD: name entry is not a string.");
+ return nullptr;
+ }
+ auto maybe_fnt = names_mask_array->GetItemAtIndexAsInteger<
+ std::underlying_type<FunctionNameType>::type>(i);
+ if (!maybe_fnt) {
+ error.SetErrorString("BRN::CFSD: name mask entry is not an integer.");
+ return nullptr;
+ }
+ names.push_back(std::string(*maybe_name));
+ name_masks.push_back(static_cast<FunctionNameType>(*maybe_fnt));
+ }
+
+ std::shared_ptr<BreakpointResolverName> resolver_sp =
+ std::make_shared<BreakpointResolverName>(
+ nullptr, names[0].c_str(), name_masks[0], language,
+ Breakpoint::MatchType::Exact, offset, skip_prologue);
+ for (size_t i = 1; i < num_elem; i++) {
+ resolver_sp->AddNameLookup(ConstString(names[i]), name_masks[i]);
+ }
+ return resolver_sp;
+ }
+}
+
+StructuredData::ObjectSP BreakpointResolverName::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ if (m_regex.IsValid()) {
+ options_dict_sp->AddStringItem(GetKey(OptionNames::RegexString),
+ m_regex.GetText());
+ } else {
+ StructuredData::ArraySP names_sp(new StructuredData::Array());
+ StructuredData::ArraySP name_masks_sp(new StructuredData::Array());
+ for (auto lookup : m_lookups) {
+ names_sp->AddItem(StructuredData::StringSP(
+ new StructuredData::String(lookup.GetName().GetStringRef())));
+ name_masks_sp->AddItem(StructuredData::UnsignedIntegerSP(
+ new StructuredData::UnsignedInteger(lookup.GetNameTypeMask())));
+ }
+ options_dict_sp->AddItem(GetKey(OptionNames::SymbolNameArray), names_sp);
+ options_dict_sp->AddItem(GetKey(OptionNames::NameMaskArray), name_masks_sp);
+ }
+ if (m_language != eLanguageTypeUnknown)
+ options_dict_sp->AddStringItem(
+ GetKey(OptionNames::LanguageName),
+ Language::GetNameForLanguageType(m_language));
+ options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
+ m_skip_prologue);
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+void BreakpointResolverName::AddNameLookup(ConstString name,
+ FunctionNameType name_type_mask) {
+
+ Module::LookupInfo lookup(name, name_type_mask, m_language);
+ m_lookups.emplace_back(lookup);
+
+ auto add_variant_funcs = [&](Language *lang) {
+ for (Language::MethodNameVariant variant :
+ lang->GetMethodNameVariants(name)) {
+ // FIXME: Should we be adding variants that aren't of type Full?
+ if (variant.GetType() & lldb::eFunctionNameTypeFull) {
+ Module::LookupInfo variant_lookup(name, variant.GetType(),
+ lang->GetLanguageType());
+ variant_lookup.SetLookupName(variant.GetName());
+ m_lookups.emplace_back(variant_lookup);
+ }
+ }
+ return true;
+ };
+
+ if (Language *lang = Language::FindPlugin(m_language)) {
+ add_variant_funcs(lang);
+ } else {
+ // Most likely m_language is eLanguageTypeUnknown. We check each language for
+ // possible variants or more qualified names and create lookups for those as
+ // well.
+ Language::ForEach(add_variant_funcs);
+ }
+}
+
+// FIXME: Right now we look at the module level, and call the module's
+// "FindFunctions".
+// Greg says he will add function tables, maybe at the CompileUnit level to
+// accelerate function lookup. At that point, we should switch the depth to
+// CompileUnit, and look in these tables.
+
+Searcher::CallbackReturn
+BreakpointResolverName::SearchCallback(SearchFilter &filter,
+ SymbolContext &context, Address *addr) {
+ Log *log = GetLog(LLDBLog::Breakpoints);
+
+ if (m_class_name) {
+ if (log)
+ log->Warning("Class/method function specification not supported yet.\n");
+ return Searcher::eCallbackReturnStop;
+ }
+
+ SymbolContextList func_list;
+ bool filter_by_cu =
+ (filter.GetFilterRequiredItems() & eSymbolContextCompUnit) != 0;
+ bool filter_by_language = (m_language != eLanguageTypeUnknown);
+
+ ModuleFunctionSearchOptions function_options;
+ function_options.include_symbols = !filter_by_cu;
+ function_options.include_inlines = true;
+
+ switch (m_match_type) {
+ case Breakpoint::Exact:
+ if (context.module_sp) {
+ for (const auto &lookup : m_lookups) {
+ const size_t start_func_idx = func_list.GetSize();
+ context.module_sp->FindFunctions(lookup, CompilerDeclContext(),
+ function_options, func_list);
+
+ const size_t end_func_idx = func_list.GetSize();
+
+ if (start_func_idx < end_func_idx)
+ lookup.Prune(func_list, start_func_idx);
+ }
+ }
+ break;
+ case Breakpoint::Regexp:
+ if (context.module_sp) {
+ context.module_sp->FindFunctions(m_regex, function_options, func_list);
+ }
+ break;
+ case Breakpoint::Glob:
+ if (log)
+ log->Warning("glob is not supported yet.");
+ break;
+ }
+
+ // If the filter specifies a Compilation Unit, remove the ones that don't
+ // pass at this point.
+ if (filter_by_cu || filter_by_language) {
+ uint32_t num_functions = func_list.GetSize();
+
+ for (size_t idx = 0; idx < num_functions; idx++) {
+ bool remove_it = false;
+ SymbolContext sc;
+ func_list.GetContextAtIndex(idx, sc);
+ if (filter_by_cu) {
+ if (!sc.comp_unit || !filter.CompUnitPasses(*sc.comp_unit))
+ remove_it = true;
+ }
+
+ if (filter_by_language) {
+ LanguageType sym_language = sc.GetLanguage();
+ if ((Language::GetPrimaryLanguage(sym_language) !=
+ Language::GetPrimaryLanguage(m_language)) &&
+ (sym_language != eLanguageTypeUnknown)) {
+ remove_it = true;
+ }
+ }
+
+ if (remove_it) {
+ func_list.RemoveContextAtIndex(idx);
+ num_functions--;
+ idx--;
+ }
+ }
+ }
+
+ BreakpointSP breakpoint_sp = GetBreakpoint();
+ Breakpoint &breakpoint = *breakpoint_sp;
+ Address break_addr;
+
+ // Remove any duplicates between the function list and the symbol list
+ for (const SymbolContext &sc : func_list) {
+ bool is_reexported = false;
+
+ if (sc.block && sc.block->GetInlinedFunctionInfo()) {
+ if (!sc.block->GetStartAddress(break_addr))
+ break_addr.Clear();
+ } else if (sc.function) {
+ break_addr = sc.function->GetAddressRange().GetBaseAddress();
+ if (m_skip_prologue && break_addr.IsValid()) {
+ const uint32_t prologue_byte_size = sc.function->GetPrologueByteSize();
+ if (prologue_byte_size)
+ break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size);
+ }
+ } else if (sc.symbol) {
+ if (sc.symbol->GetType() == eSymbolTypeReExported) {
+ const Symbol *actual_symbol =
+ sc.symbol->ResolveReExportedSymbol(breakpoint.GetTarget());
+ if (actual_symbol) {
+ is_reexported = true;
+ break_addr = actual_symbol->GetAddress();
+ }
+ } else {
+ break_addr = sc.symbol->GetAddress();
+ }
+
+ if (m_skip_prologue && break_addr.IsValid()) {
+ const uint32_t prologue_byte_size = sc.symbol->GetPrologueByteSize();
+ if (prologue_byte_size)
+ break_addr.SetOffset(break_addr.GetOffset() + prologue_byte_size);
+ else {
+ const Architecture *arch =
+ breakpoint.GetTarget().GetArchitecturePlugin();
+ if (arch)
+ arch->AdjustBreakpointAddress(*sc.symbol, break_addr);
+ }
+ }
+ }
+
+ if (!break_addr.IsValid())
+ continue;
+
+ if (!filter.AddressPasses(break_addr))
+ continue;
+
+ bool new_location;
+ BreakpointLocationSP bp_loc_sp(AddLocation(break_addr, &new_location));
+ bp_loc_sp->SetIsReExported(is_reexported);
+ if (bp_loc_sp && new_location && !breakpoint.IsInternal()) {
+ if (log) {
+ StreamString s;
+ bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ LLDB_LOGF(log, "Added location: %s\n", s.GetData());
+ }
+ }
+ }
+
+ return Searcher::eCallbackReturnContinue;
+}
+
+lldb::SearchDepth BreakpointResolverName::GetDepth() {
+ return lldb::eSearchDepthModule;
+}
+
+void BreakpointResolverName::GetDescription(Stream *s) {
+ if (m_match_type == Breakpoint::Regexp)
+ s->Printf("regex = '%s'", m_regex.GetText().str().c_str());
+ else {
+ size_t num_names = m_lookups.size();
+ if (num_names == 1)
+ s->Printf("name = '%s'", m_lookups[0].GetName().GetCString());
+ else {
+ s->Printf("names = {");
+ for (size_t i = 0; i < num_names; i++) {
+ s->Printf("%s'%s'", (i == 0 ? "" : ", "),
+ m_lookups[i].GetName().GetCString());
+ }
+ s->Printf("}");
+ }
+ }
+ if (m_language != eLanguageTypeUnknown) {
+ s->Printf(", language = %s", Language::GetNameForLanguageType(m_language));
+ }
+}
+
+void BreakpointResolverName::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverName::CopyForBreakpoint(BreakpointSP &breakpoint) {
+ lldb::BreakpointResolverSP ret_sp(new BreakpointResolverName(*this));
+ ret_sp->SetBreakpoint(breakpoint);
+ return ret_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
new file mode 100644
index 000000000000..dbfa8b8572f2
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointResolverScripted.cpp
@@ -0,0 +1,151 @@
+//===-- BreakpointResolverScripted.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/BreakpointResolverScripted.h"
+
+
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/StructuredDataImpl.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/StreamString.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// BreakpointResolverScripted:
+BreakpointResolverScripted::BreakpointResolverScripted(
+ const BreakpointSP &bkpt, const llvm::StringRef class_name,
+ lldb::SearchDepth depth, const StructuredDataImpl &args_data)
+ : BreakpointResolver(bkpt, BreakpointResolver::PythonResolver),
+ m_class_name(std::string(class_name)), m_depth(depth), m_args(args_data) {
+ CreateImplementationIfNeeded(bkpt);
+}
+
+void BreakpointResolverScripted::CreateImplementationIfNeeded(
+ BreakpointSP breakpoint_sp) {
+ if (m_implementation_sp)
+ return;
+
+ if (m_class_name.empty())
+ return;
+
+ if (!breakpoint_sp)
+ return;
+
+ TargetSP target_sp = breakpoint_sp->GetTargetSP();
+ ScriptInterpreter *script_interp = target_sp->GetDebugger()
+ .GetScriptInterpreter();
+ if (!script_interp)
+ return;
+
+ m_implementation_sp = script_interp->CreateScriptedBreakpointResolver(
+ m_class_name.c_str(), m_args, breakpoint_sp);
+}
+
+void BreakpointResolverScripted::NotifyBreakpointSet() {
+ CreateImplementationIfNeeded(GetBreakpoint());
+}
+
+BreakpointResolverSP BreakpointResolverScripted::CreateFromStructuredData(
+ const StructuredData::Dictionary &options_dict, Status &error) {
+ llvm::StringRef class_name;
+ bool success;
+
+ success = options_dict.GetValueForKeyAsString(
+ GetKey(OptionNames::PythonClassName), class_name);
+ if (!success) {
+ error.SetErrorString("BRFL::CFSD: Couldn't find class name entry.");
+ return nullptr;
+ }
+ // The Python function will actually provide the search depth, this is a
+ // placeholder.
+ lldb::SearchDepth depth = lldb::eSearchDepthTarget;
+
+ StructuredDataImpl args_data_impl;
+ StructuredData::Dictionary *args_dict = nullptr;
+ if (options_dict.GetValueForKeyAsDictionary(GetKey(OptionNames::ScriptArgs),
+ args_dict))
+ args_data_impl.SetObjectSP(args_dict->shared_from_this());
+ return std::make_shared<BreakpointResolverScripted>(nullptr, class_name,
+ depth, args_data_impl);
+}
+
+StructuredData::ObjectSP
+BreakpointResolverScripted::SerializeToStructuredData() {
+ StructuredData::DictionarySP options_dict_sp(
+ new StructuredData::Dictionary());
+
+ options_dict_sp->AddStringItem(GetKey(OptionNames::PythonClassName),
+ m_class_name);
+ if (m_args.IsValid())
+ options_dict_sp->AddItem(GetKey(OptionNames::ScriptArgs),
+ m_args.GetObjectSP());
+
+ return WrapOptionsDict(options_dict_sp);
+}
+
+ScriptInterpreter *BreakpointResolverScripted::GetScriptInterpreter() {
+ return GetBreakpoint()->GetTarget().GetDebugger().GetScriptInterpreter();
+}
+
+Searcher::CallbackReturn BreakpointResolverScripted::SearchCallback(
+ SearchFilter &filter, SymbolContext &context, Address *addr) {
+ bool should_continue = true;
+ if (!m_implementation_sp)
+ return Searcher::eCallbackReturnStop;
+
+ ScriptInterpreter *interp = GetScriptInterpreter();
+ should_continue = interp->ScriptedBreakpointResolverSearchCallback(
+ m_implementation_sp,
+ &context);
+ if (should_continue)
+ return Searcher::eCallbackReturnContinue;
+
+ return Searcher::eCallbackReturnStop;
+}
+
+lldb::SearchDepth
+BreakpointResolverScripted::GetDepth() {
+ lldb::SearchDepth depth = lldb::eSearchDepthModule;
+ if (m_implementation_sp) {
+ ScriptInterpreter *interp = GetScriptInterpreter();
+ depth = interp->ScriptedBreakpointResolverSearchDepth(
+ m_implementation_sp);
+ }
+ return depth;
+}
+
+void BreakpointResolverScripted::GetDescription(Stream *s) {
+ StructuredData::GenericSP generic_sp;
+ std::string short_help;
+
+ if (m_implementation_sp) {
+ ScriptInterpreter *interp = GetScriptInterpreter();
+ interp->GetShortHelpForCommandObject(m_implementation_sp,
+ short_help);
+ }
+ if (!short_help.empty())
+ s->PutCString(short_help.c_str());
+ else
+ s->Printf("python class = %s", m_class_name.c_str());
+}
+
+void BreakpointResolverScripted::Dump(Stream *s) const {}
+
+lldb::BreakpointResolverSP
+BreakpointResolverScripted::CopyForBreakpoint(BreakpointSP &breakpoint) {
+ return std::make_shared<BreakpointResolverScripted>(breakpoint, m_class_name,
+ m_depth, m_args);
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp
new file mode 100644
index 000000000000..3ca93f908e30
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/BreakpointSite.cpp
@@ -0,0 +1,206 @@
+//===-- BreakpointSite.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 <cinttypes>
+
+#include "lldb/Breakpoint/BreakpointSite.h"
+
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+BreakpointSite::BreakpointSite(const BreakpointLocationSP &constituent,
+ lldb::addr_t addr, bool use_hardware)
+ : StoppointSite(GetNextID(), addr, 0, use_hardware),
+ m_type(eSoftware), // Process subclasses need to set this correctly using
+ // SetType()
+ m_saved_opcode(), m_trap_opcode(),
+ m_enabled(false) // Need to create it disabled, so the first enable turns
+ // it on.
+{
+ m_constituents.Add(constituent);
+}
+
+BreakpointSite::~BreakpointSite() {
+ BreakpointLocationSP bp_loc_sp;
+ const size_t constituent_count = m_constituents.GetSize();
+ for (size_t i = 0; i < constituent_count; i++) {
+ m_constituents.GetByIndex(i)->ClearBreakpointSite();
+ }
+}
+
+break_id_t BreakpointSite::GetNextID() {
+ static break_id_t g_next_id = 0;
+ return ++g_next_id;
+}
+
+// RETURNS - true if we should stop at this breakpoint, false if we
+// should continue.
+
+bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) {
+ m_hit_counter.Increment();
+ // ShouldStop can do a lot of work, and might even come back and hit
+ // this breakpoint site again. So don't hold the m_constituents_mutex the
+ // whole while. Instead make a local copy of the collection and call
+ // ShouldStop on the copy.
+ BreakpointLocationCollection constituents_copy;
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ constituents_copy = m_constituents;
+ }
+ return constituents_copy.ShouldStop(context);
+}
+
+bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ const size_t constituent_count = m_constituents.GetSize();
+ for (size_t i = 0; i < constituent_count; i++) {
+ if (m_constituents.GetByIndex(i)->GetBreakpoint().GetID() == bp_id)
+ return true;
+ }
+ return false;
+}
+
+void BreakpointSite::Dump(Stream *s) const {
+ if (s == nullptr)
+ return;
+
+ s->Printf("BreakpointSite %u: addr = 0x%8.8" PRIx64
+ " type = %s breakpoint hit_count = %-4u",
+ GetID(), (uint64_t)m_addr, IsHardware() ? "hardware" : "software",
+ GetHitCount());
+}
+
+void BreakpointSite::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ if (level != lldb::eDescriptionLevelBrief)
+ s->Printf("breakpoint site: %d at 0x%8.8" PRIx64, GetID(),
+ GetLoadAddress());
+ m_constituents.GetDescription(s, level);
+}
+
+bool BreakpointSite::IsInternal() const { return m_constituents.IsInternal(); }
+
+uint8_t *BreakpointSite::GetTrapOpcodeBytes() { return &m_trap_opcode[0]; }
+
+const uint8_t *BreakpointSite::GetTrapOpcodeBytes() const {
+ return &m_trap_opcode[0];
+}
+
+size_t BreakpointSite::GetTrapOpcodeMaxByteSize() const {
+ return sizeof(m_trap_opcode);
+}
+
+bool BreakpointSite::SetTrapOpcode(const uint8_t *trap_opcode,
+ uint32_t trap_opcode_size) {
+ if (trap_opcode_size > 0 && trap_opcode_size <= sizeof(m_trap_opcode)) {
+ m_byte_size = trap_opcode_size;
+ ::memcpy(m_trap_opcode, trap_opcode, trap_opcode_size);
+ return true;
+ }
+ m_byte_size = 0;
+ return false;
+}
+
+uint8_t *BreakpointSite::GetSavedOpcodeBytes() { return &m_saved_opcode[0]; }
+
+const uint8_t *BreakpointSite::GetSavedOpcodeBytes() const {
+ return &m_saved_opcode[0];
+}
+
+bool BreakpointSite::IsEnabled() const { return m_enabled; }
+
+void BreakpointSite::SetEnabled(bool enabled) { m_enabled = enabled; }
+
+void BreakpointSite::AddConstituent(const BreakpointLocationSP &constituent) {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ m_constituents.Add(constituent);
+}
+
+size_t BreakpointSite::RemoveConstituent(lldb::break_id_t break_id,
+ lldb::break_id_t break_loc_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ m_constituents.Remove(break_id, break_loc_id);
+ return m_constituents.GetSize();
+}
+
+size_t BreakpointSite::GetNumberOfConstituents() {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ return m_constituents.GetSize();
+}
+
+BreakpointLocationSP BreakpointSite::GetConstituentAtIndex(size_t index) {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ return m_constituents.GetByIndex(index);
+}
+
+bool BreakpointSite::ValidForThisThread(Thread &thread) {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ return m_constituents.ValidForThisThread(thread);
+}
+
+void BreakpointSite::BumpHitCounts() {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ for (BreakpointLocationSP loc_sp : m_constituents.BreakpointLocations()) {
+ loc_sp->BumpHitCount();
+ }
+}
+
+bool BreakpointSite::IntersectsRange(lldb::addr_t addr, size_t size,
+ lldb::addr_t *intersect_addr,
+ size_t *intersect_size,
+ size_t *opcode_offset) const {
+ // The function should be called only for software breakpoints.
+ lldbassert(GetType() == Type::eSoftware);
+
+ if (m_byte_size == 0)
+ return false;
+
+ const lldb::addr_t bp_end_addr = m_addr + m_byte_size;
+ const lldb::addr_t end_addr = addr + size;
+ // Is the breakpoint end address before the passed in start address?
+ if (bp_end_addr <= addr)
+ return false;
+
+ // Is the breakpoint start address after passed in end address?
+ if (end_addr <= m_addr)
+ return false;
+
+ if (intersect_addr || intersect_size || opcode_offset) {
+ if (m_addr < addr) {
+ if (intersect_addr)
+ *intersect_addr = addr;
+ if (intersect_size)
+ *intersect_size =
+ std::min<lldb::addr_t>(bp_end_addr, end_addr) - addr;
+ if (opcode_offset)
+ *opcode_offset = addr - m_addr;
+ } else {
+ if (intersect_addr)
+ *intersect_addr = m_addr;
+ if (intersect_size)
+ *intersect_size =
+ std::min<lldb::addr_t>(bp_end_addr, end_addr) - m_addr;
+ if (opcode_offset)
+ *opcode_offset = 0;
+ }
+ }
+ return true;
+}
+
+size_t BreakpointSite::CopyConstituentsList(
+ BreakpointLocationCollection &out_collection) {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ for (BreakpointLocationSP loc_sp : m_constituents.BreakpointLocations()) {
+ out_collection.Add(loc_sp);
+ }
+ return out_collection.GetSize();
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/StopPointSiteList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/StopPointSiteList.cpp
new file mode 100644
index 000000000000..275d0fbf265a
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/StopPointSiteList.cpp
@@ -0,0 +1,37 @@
+//===-- StopPointSiteList.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/StopPointSiteList.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Breakpoint/WatchpointResource.h"
+
+#include "lldb/Utility/Stream.h"
+#include <algorithm>
+
+using namespace lldb;
+using namespace lldb_private;
+
+// This method is only defined when we're specializing for
+// BreakpointSite / BreakpointLocation / Breakpoint.
+// Watchpoints don't have a similar structure, they are
+// WatchpointResource / Watchpoint
+
+template <>
+bool StopPointSiteList<BreakpointSite>::StopPointSiteContainsBreakpoint(
+ typename BreakpointSite::SiteID site_id, lldb::break_id_t bp_id) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ typename collection::const_iterator pos = GetIDConstIterator(site_id);
+ if (pos != m_site_list.end())
+ return pos->second->IsBreakpointAtThisSite(bp_id);
+
+ return false;
+}
+
+namespace lldb_private {
+template class StopPointSiteList<BreakpointSite>;
+} // namespace lldb_private
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp b/contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp
new file mode 100644
index 000000000000..94e97cd4542f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/Stoppoint.cpp
@@ -0,0 +1,24 @@
+//===-- Stoppoint.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/Stoppoint.h"
+#include "lldb/lldb-private.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Stoppoint constructor
+Stoppoint::Stoppoint() = default;
+
+// Destructor
+Stoppoint::~Stoppoint() = default;
+
+break_id_t Stoppoint::GetID() const { return m_bid; }
+
+void Stoppoint::SetID(break_id_t bid) { m_bid = bid; }
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp b/contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp
new file mode 100644
index 000000000000..926c0afe8fc1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/StoppointCallbackContext.cpp
@@ -0,0 +1,23 @@
+//===-- StoppointCallbackContext.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+
+using namespace lldb_private;
+
+StoppointCallbackContext::StoppointCallbackContext() = default;
+
+StoppointCallbackContext::StoppointCallbackContext(
+ Event *e, const ExecutionContext &exe_ctx, bool synchronously)
+ : event(e), exe_ctx_ref(exe_ctx), is_synchronous(synchronously) {}
+
+void StoppointCallbackContext::Clear() {
+ event = nullptr;
+ exe_ctx_ref.Clear();
+ is_synchronous = false;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/StoppointSite.cpp b/contrib/llvm-project/lldb/source/Breakpoint/StoppointSite.cpp
new file mode 100644
index 000000000000..a28b6b927a1f
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/StoppointSite.cpp
@@ -0,0 +1,22 @@
+//===-- StoppointSite.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/StoppointSite.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+StoppointSite::StoppointSite(break_id_t id, addr_t addr, bool hardware)
+ : m_id(id), m_addr(addr), m_is_hardware_required(hardware), m_byte_size(0),
+ m_hit_counter() {}
+
+StoppointSite::StoppointSite(break_id_t id, addr_t addr, uint32_t byte_size,
+ bool hardware)
+ : m_id(id), m_addr(addr), m_is_hardware_required(hardware),
+ m_byte_size(byte_size), m_hit_counter() {}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp b/contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp
new file mode 100644
index 000000000000..715e83c76697
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp
@@ -0,0 +1,550 @@
+//===-- Watchpoint.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/Watchpoint.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/WatchpointResource.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Core/ValueObjectMemory.h"
+#include "lldb/DataFormatters/DumpValueObjectOptions.h"
+#include "lldb/Expression/UserExpression.h"
+#include "lldb/Symbol/TypeSystem.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,
+ const CompilerType *type, bool hardware)
+ : StoppointSite(0, addr, size, hardware), m_target(target),
+ m_enabled(false), m_is_hardware(hardware), m_is_watch_variable(false),
+ m_is_ephemeral(false), m_disabled_count(0), m_watch_read(0),
+ m_watch_write(0), m_watch_modify(0), m_ignore_count(0) {
+
+ if (type && type->IsValid())
+ m_type = *type;
+ else {
+ // If we don't have a known type, then we force it to unsigned int of the
+ // right size.
+ auto type_system_or_err =
+ target.GetScratchTypeSystemForLanguage(eLanguageTypeC);
+ if (auto err = type_system_or_err.takeError()) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
+ "Failed to set type: {0}");
+ } else {
+ if (auto ts = *type_system_or_err) {
+ if (size <= target.GetArchitecture().GetAddressByteSize()) {
+ m_type =
+ ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);
+ } else {
+ CompilerType clang_uint8_type =
+ ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
+ m_type = clang_uint8_type.GetArrayType(size);
+ }
+ } else
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
+ "Failed to set type: Typesystem is no longer live: {0}");
+ }
+ }
+
+ // Set the initial value of the watched variable:
+ if (m_target.GetProcessSP()) {
+ ExecutionContext exe_ctx;
+ m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx);
+ CaptureWatchedValue(exe_ctx);
+ }
+}
+
+Watchpoint::~Watchpoint() = default;
+
+// This function is used when "baton" doesn't need to be freed
+void Watchpoint::SetCallback(WatchpointHitCallback 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);
+
+ SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
+}
+
+// This function is used when a baton needs to be freed and therefore is
+// contained in a "Baton" subclass.
+void Watchpoint::SetCallback(WatchpointHitCallback callback,
+ const BatonSP &callback_baton_sp,
+ bool is_synchronous) {
+ m_options.SetCallback(callback, callback_baton_sp, is_synchronous);
+ SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
+}
+
+bool Watchpoint::SetupVariableWatchpointDisabler(StackFrameSP frame_sp) const {
+ if (!frame_sp)
+ return false;
+
+ ThreadSP thread_sp = frame_sp->GetThread();
+ if (!thread_sp)
+ return false;
+
+ uint32_t return_frame_index =
+ thread_sp->GetSelectedFrameIndex(DoNoSelectMostRelevantFrame) + 1;
+ if (return_frame_index >= LLDB_INVALID_FRAME_ID)
+ return false;
+
+ StackFrameSP return_frame_sp(
+ thread_sp->GetStackFrameAtIndex(return_frame_index));
+ if (!return_frame_sp)
+ return false;
+
+ ExecutionContext exe_ctx(return_frame_sp);
+ TargetSP target_sp = exe_ctx.GetTargetSP();
+ if (!target_sp)
+ return false;
+
+ Address return_address(return_frame_sp->GetFrameCodeAddress());
+ lldb::addr_t return_addr = return_address.GetLoadAddress(target_sp.get());
+ if (return_addr == LLDB_INVALID_ADDRESS)
+ return false;
+
+ BreakpointSP bp_sp = target_sp->CreateBreakpoint(
+ return_addr, /*internal=*/true, /*request_hardware=*/false);
+ if (!bp_sp || !bp_sp->HasResolvedLocations())
+ return false;
+
+ auto wvc_up = std::make_unique<WatchpointVariableContext>(GetID(), exe_ctx);
+ auto baton_sp = std::make_shared<WatchpointVariableBaton>(std::move(wvc_up));
+ bp_sp->SetCallback(VariableWatchpointDisabler, baton_sp);
+ bp_sp->SetOneShot(true);
+ bp_sp->SetBreakpointKind("variable watchpoint disabler");
+ return true;
+}
+
+bool Watchpoint::VariableWatchpointDisabler(void *baton,
+ StoppointCallbackContext *context,
+ user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(baton && "null baton");
+ if (!baton || !context)
+ return false;
+
+ Log *log = GetLog(LLDBLog::Watchpoints);
+
+ WatchpointVariableContext *wvc =
+ static_cast<WatchpointVariableContext *>(baton);
+
+ LLDB_LOGF(log, "called by breakpoint %" PRIu64 ".%" PRIu64, break_id,
+ break_loc_id);
+
+ if (wvc->watch_id == LLDB_INVALID_WATCH_ID)
+ return false;
+
+ TargetSP target_sp = context->exe_ctx_ref.GetTargetSP();
+ if (!target_sp)
+ return false;
+
+ ProcessSP process_sp = target_sp->GetProcessSP();
+ if (!process_sp)
+ return false;
+
+ WatchpointSP watch_sp =
+ target_sp->GetWatchpointList().FindByID(wvc->watch_id);
+ if (!watch_sp)
+ return false;
+
+ if (wvc->exe_ctx == context->exe_ctx_ref) {
+ LLDB_LOGF(log,
+ "callback for watchpoint %" PRId32
+ " matched internal breakpoint execution context",
+ watch_sp->GetID());
+ process_sp->DisableWatchpoint(watch_sp);
+ return false;
+ }
+ LLDB_LOGF(log,
+ "callback for watchpoint %" PRId32
+ " didn't match internal breakpoint execution context",
+ watch_sp->GetID());
+ return false;
+}
+
+void Watchpoint::ClearCallback() {
+ m_options.ClearCallback();
+ SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
+}
+
+void Watchpoint::SetDeclInfo(const std::string &str) { m_decl_str = str; }
+
+std::string Watchpoint::GetWatchSpec() { return m_watch_spec_str; }
+
+void Watchpoint::SetWatchSpec(const std::string &str) {
+ m_watch_spec_str = str;
+}
+
+bool Watchpoint::IsHardware() const {
+ lldbassert(m_is_hardware || !HardwareRequired());
+ return m_is_hardware;
+}
+
+bool Watchpoint::IsWatchVariable() const { return m_is_watch_variable; }
+
+void Watchpoint::SetWatchVariable(bool val) { m_is_watch_variable = val; }
+
+bool Watchpoint::CaptureWatchedValue(const ExecutionContext &exe_ctx) {
+ ConstString g_watch_name("$__lldb__watch_value");
+ m_old_value_sp = m_new_value_sp;
+ Address watch_address(GetLoadAddress());
+ if (!m_type.IsValid()) {
+ // Don't know how to report new & old values, since we couldn't make a
+ // scalar type for this watchpoint. This works around an assert in
+ // ValueObjectMemory::Create.
+ // FIXME: This should not happen, but if it does in some case we care about,
+ // we can go grab the value raw and print it as unsigned.
+ return false;
+ }
+ m_new_value_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),
+ watch_address, m_type);
+ m_new_value_sp = m_new_value_sp->CreateConstantValue(g_watch_name);
+ return (m_new_value_sp && m_new_value_sp->GetError().Success());
+}
+
+bool Watchpoint::WatchedValueReportable(const ExecutionContext &exe_ctx) {
+ if (!m_watch_modify || m_watch_read)
+ return true;
+ if (!m_type.IsValid())
+ return true;
+
+ ConstString g_watch_name("$__lldb__watch_value");
+ Address watch_address(GetLoadAddress());
+ ValueObjectSP newest_valueobj_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),
+ watch_address, m_type);
+ newest_valueobj_sp = newest_valueobj_sp->CreateConstantValue(g_watch_name);
+ Status error;
+
+ DataExtractor new_data;
+ DataExtractor old_data;
+
+ newest_valueobj_sp->GetData(new_data, error);
+ if (error.Fail())
+ return true;
+ m_new_value_sp->GetData(old_data, error);
+ if (error.Fail())
+ return true;
+
+ if (new_data.GetByteSize() != old_data.GetByteSize() ||
+ new_data.GetByteSize() == 0)
+ return true;
+
+ if (memcmp(new_data.GetDataStart(), old_data.GetDataStart(),
+ old_data.GetByteSize()) == 0)
+ return false; // Value has not changed, user requested modify watchpoint
+
+ return true;
+}
+
+// RETURNS - true if we should stop at this breakpoint, false if we
+// should continue.
+
+bool Watchpoint::ShouldStop(StoppointCallbackContext *context) {
+ m_hit_counter.Increment();
+
+ return IsEnabled();
+}
+
+void Watchpoint::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ DumpWithLevel(s, level);
+}
+
+void Watchpoint::Dump(Stream *s) const {
+ DumpWithLevel(s, lldb::eDescriptionLevelBrief);
+}
+
+// If prefix is nullptr, we display the watch id and ignore the prefix
+// altogether.
+bool Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const {
+ bool printed_anything = false;
+
+ // For read watchpoints, don't display any before/after value changes.
+ if (m_watch_read && !m_watch_modify && !m_watch_write)
+ return printed_anything;
+
+ s->Printf("\n");
+ s->Printf("Watchpoint %u hit:\n", GetID());
+
+ StreamString values_ss;
+ if (prefix)
+ values_ss.Indent(prefix);
+
+ if (m_old_value_sp) {
+ if (auto *old_value_cstr = m_old_value_sp->GetValueAsCString()) {
+ values_ss.Printf("old value: %s", old_value_cstr);
+ } else {
+ if (auto *old_summary_cstr = m_old_value_sp->GetSummaryAsCString())
+ values_ss.Printf("old value: %s", old_summary_cstr);
+ else {
+ StreamString strm;
+ DumpValueObjectOptions options;
+ options.SetUseDynamicType(eNoDynamicValues)
+ .SetHideRootType(true)
+ .SetHideRootName(true)
+ .SetHideName(true);
+ if (llvm::Error error = m_old_value_sp->Dump(strm, options))
+ strm << "error: " << toString(std::move(error));
+
+ if (strm.GetData())
+ values_ss.Printf("old value: %s", strm.GetData());
+ }
+ }
+ }
+
+ if (m_new_value_sp) {
+ if (values_ss.GetSize())
+ values_ss.Printf("\n");
+
+ if (auto *new_value_cstr = m_new_value_sp->GetValueAsCString())
+ values_ss.Printf("new value: %s", new_value_cstr);
+ else {
+ if (auto *new_summary_cstr = m_new_value_sp->GetSummaryAsCString())
+ values_ss.Printf("new value: %s", new_summary_cstr);
+ else {
+ StreamString strm;
+ DumpValueObjectOptions options;
+ options.SetUseDynamicType(eNoDynamicValues)
+ .SetHideRootType(true)
+ .SetHideRootName(true)
+ .SetHideName(true);
+ if (llvm::Error error = m_new_value_sp->Dump(strm, options))
+ strm << "error: " << toString(std::move(error));
+
+ if (strm.GetData())
+ values_ss.Printf("new value: %s", strm.GetData());
+ }
+ }
+ }
+
+ if (values_ss.GetSize()) {
+ s->Printf("%s", values_ss.GetData());
+ printed_anything = true;
+ }
+
+ return printed_anything;
+}
+
+void Watchpoint::DumpWithLevel(Stream *s,
+ lldb::DescriptionLevel description_level) const {
+ if (s == nullptr)
+ return;
+
+ assert(description_level >= lldb::eDescriptionLevelBrief &&
+ description_level <= lldb::eDescriptionLevelVerbose);
+
+ s->Printf("Watchpoint %u: addr = 0x%8.8" PRIx64
+ " size = %u state = %s type = %s%s%s",
+ GetID(), GetLoadAddress(), m_byte_size,
+ IsEnabled() ? "enabled" : "disabled", m_watch_read ? "r" : "",
+ m_watch_write ? "w" : "", m_watch_modify ? "m" : "");
+
+ if (description_level >= lldb::eDescriptionLevelFull) {
+ if (!m_decl_str.empty())
+ s->Printf("\n declare @ '%s'", m_decl_str.c_str());
+ if (!m_watch_spec_str.empty())
+ s->Printf("\n watchpoint spec = '%s'", m_watch_spec_str.c_str());
+ if (IsEnabled()) {
+ if (ProcessSP process_sp = m_target.GetProcessSP()) {
+ auto &resourcelist = process_sp->GetWatchpointResourceList();
+ size_t idx = 0;
+ s->Printf("\n watchpoint resources:");
+ for (WatchpointResourceSP &wpres : resourcelist.Sites()) {
+ if (wpres->ConstituentsContains(this)) {
+ s->Printf("\n #%zu: ", idx);
+ wpres->Dump(s);
+ }
+ idx++;
+ }
+ }
+ }
+
+ // Dump the snapshots we have taken.
+ DumpSnapshots(s, " ");
+
+ if (GetConditionText())
+ s->Printf("\n condition = '%s'", GetConditionText());
+ m_options.GetCallbackDescription(s, description_level);
+ }
+
+ if (description_level >= lldb::eDescriptionLevelVerbose) {
+ s->Printf("\n hit_count = %-4u ignore_count = %-4u", GetHitCount(),
+ GetIgnoreCount());
+ }
+}
+
+bool Watchpoint::IsEnabled() const { return m_enabled; }
+
+// Within StopInfo.cpp, we purposely turn on the ephemeral mode right before
+// temporarily disable the watchpoint in order to perform possible watchpoint
+// actions without triggering further watchpoint events. After the temporary
+// disabled watchpoint is enabled, we then turn off the ephemeral mode.
+
+void Watchpoint::TurnOnEphemeralMode() { m_is_ephemeral = true; }
+
+void Watchpoint::TurnOffEphemeralMode() {
+ m_is_ephemeral = false;
+ // Leaving ephemeral mode, reset the m_disabled_count!
+ m_disabled_count = 0;
+}
+
+bool Watchpoint::IsDisabledDuringEphemeralMode() {
+ return m_disabled_count > 1 && m_is_ephemeral;
+}
+
+void Watchpoint::SetEnabled(bool enabled, bool notify) {
+ if (!enabled) {
+ if (m_is_ephemeral)
+ ++m_disabled_count;
+
+ // Don't clear the snapshots for now.
+ // Within StopInfo.cpp, we purposely do disable/enable watchpoint while
+ // performing watchpoint actions.
+ }
+ bool changed = enabled != m_enabled;
+ m_enabled = enabled;
+ if (notify && !m_is_ephemeral && changed)
+ SendWatchpointChangedEvent(enabled ? eWatchpointEventTypeEnabled
+ : eWatchpointEventTypeDisabled);
+}
+
+void Watchpoint::SetWatchpointType(uint32_t type, bool notify) {
+ int old_watch_read = m_watch_read;
+ int old_watch_write = m_watch_write;
+ int old_watch_modify = m_watch_modify;
+ m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0;
+ m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0;
+ m_watch_modify = (type & LLDB_WATCH_TYPE_MODIFY) != 0;
+ if (notify &&
+ (old_watch_read != m_watch_read || old_watch_write != m_watch_write ||
+ old_watch_modify != m_watch_modify))
+ SendWatchpointChangedEvent(eWatchpointEventTypeTypeChanged);
+}
+
+bool Watchpoint::WatchpointRead() const { return m_watch_read != 0; }
+
+bool Watchpoint::WatchpointWrite() const { return m_watch_write != 0; }
+
+bool Watchpoint::WatchpointModify() const { return m_watch_modify != 0; }
+
+uint32_t Watchpoint::GetIgnoreCount() const { return m_ignore_count; }
+
+void Watchpoint::SetIgnoreCount(uint32_t n) {
+ bool changed = m_ignore_count != n;
+ m_ignore_count = n;
+ if (changed)
+ SendWatchpointChangedEvent(eWatchpointEventTypeIgnoreChanged);
+}
+
+bool Watchpoint::InvokeCallback(StoppointCallbackContext *context) {
+ return m_options.InvokeCallback(context, GetID());
+}
+
+void Watchpoint::SetCondition(const char *condition) {
+ if (condition == nullptr || condition[0] == '\0') {
+ if (m_condition_up)
+ m_condition_up.reset();
+ } else {
+ // Pass nullptr for expr_prefix (no translation-unit level definitions).
+ Status error;
+ m_condition_up.reset(m_target.GetUserExpressionForLanguage(
+ condition, {}, {}, UserExpression::eResultTypeAny,
+ EvaluateExpressionOptions(), nullptr, error));
+ if (error.Fail()) {
+ // FIXME: Log something...
+ m_condition_up.reset();
+ }
+ }
+ SendWatchpointChangedEvent(eWatchpointEventTypeConditionChanged);
+}
+
+const char *Watchpoint::GetConditionText() const {
+ if (m_condition_up)
+ return m_condition_up->GetUserText();
+ else
+ return nullptr;
+}
+
+void Watchpoint::SendWatchpointChangedEvent(
+ lldb::WatchpointEventType eventKind) {
+ if (GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitWatchpointChanged)) {
+ auto data_sp =
+ std::make_shared<WatchpointEventData>(eventKind, shared_from_this());
+ GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, data_sp);
+ }
+}
+
+Watchpoint::WatchpointEventData::WatchpointEventData(
+ WatchpointEventType sub_type, const WatchpointSP &new_watchpoint_sp)
+ : m_watchpoint_event(sub_type), m_new_watchpoint_sp(new_watchpoint_sp) {}
+
+Watchpoint::WatchpointEventData::~WatchpointEventData() = default;
+
+llvm::StringRef Watchpoint::WatchpointEventData::GetFlavorString() {
+ return "Watchpoint::WatchpointEventData";
+}
+
+llvm::StringRef Watchpoint::WatchpointEventData::GetFlavor() const {
+ return WatchpointEventData::GetFlavorString();
+}
+
+WatchpointSP &Watchpoint::WatchpointEventData::GetWatchpoint() {
+ return m_new_watchpoint_sp;
+}
+
+WatchpointEventType
+Watchpoint::WatchpointEventData::GetWatchpointEventType() const {
+ return m_watchpoint_event;
+}
+
+void Watchpoint::WatchpointEventData::Dump(Stream *s) const {}
+
+const Watchpoint::WatchpointEventData *
+Watchpoint::WatchpointEventData::GetEventDataFromEvent(const Event *event) {
+ if (event) {
+ const EventData *event_data = event->GetData();
+ if (event_data &&
+ event_data->GetFlavor() == WatchpointEventData::GetFlavorString())
+ return static_cast<const WatchpointEventData *>(event->GetData());
+ }
+ return nullptr;
+}
+
+WatchpointEventType
+Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent(
+ const EventSP &event_sp) {
+ const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());
+
+ if (data == nullptr)
+ return eWatchpointEventTypeInvalidType;
+ else
+ return data->GetWatchpointEventType();
+}
+
+WatchpointSP Watchpoint::WatchpointEventData::GetWatchpointFromEvent(
+ const EventSP &event_sp) {
+ WatchpointSP wp_sp;
+
+ const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());
+ if (data)
+ wp_sp = data->m_new_watchpoint_sp;
+
+ return wp_sp;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/WatchpointAlgorithms.cpp b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointAlgorithms.cpp
new file mode 100644
index 000000000000..3caf29b04317
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointAlgorithms.cpp
@@ -0,0 +1,158 @@
+//===-- WatchpointAlgorithms.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/WatchpointAlgorithms.h"
+#include "lldb/Breakpoint/WatchpointResource.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+std::vector<WatchpointResourceSP>
+WatchpointAlgorithms::AtomizeWatchpointRequest(
+ addr_t addr, size_t size, bool read, bool write,
+ WatchpointHardwareFeature supported_features, ArchSpec &arch) {
+
+ std::vector<Region> entries;
+
+ if (supported_features & eWatchpointHardwareArmMASK) {
+ entries =
+ PowerOf2Watchpoints(addr, size,
+ /*min_byte_size*/ 1,
+ /*max_byte_size*/ INT32_MAX,
+ /*address_byte_size*/ arch.GetAddressByteSize());
+ } else {
+ // As a fallback, assume we can watch any power-of-2
+ // number of bytes up through the size of an address in the target.
+ entries =
+ PowerOf2Watchpoints(addr, size,
+ /*min_byte_size*/ 1,
+ /*max_byte_size*/ arch.GetAddressByteSize(),
+ /*address_byte_size*/ arch.GetAddressByteSize());
+ }
+
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOGV(log, "AtomizeWatchpointRequest user request addr {0:x} size {1}",
+ addr, size);
+ std::vector<WatchpointResourceSP> resources;
+ for (Region &ent : entries) {
+ LLDB_LOGV(log, "AtomizeWatchpointRequest creating resource {0:x} size {1}",
+ ent.addr, ent.size);
+ WatchpointResourceSP wp_res_sp =
+ std::make_shared<WatchpointResource>(ent.addr, ent.size, read, write);
+ resources.push_back(wp_res_sp);
+ }
+
+ return resources;
+}
+
+// This should be `std::bit_ceil(aligned_size)` but
+// that requires C++20.
+// Calculates the smallest integral power of two that is not smaller than x.
+static uint64_t bit_ceil(uint64_t input) {
+ if (input <= 1 || llvm::popcount(input) == 1)
+ return input;
+
+ return 1ULL << (64 - llvm::countl_zero(input));
+}
+
+/// Convert a user's watchpoint request (\a user_addr and \a user_size)
+/// into hardware watchpoints, for a target that can watch a power-of-2
+/// region of memory (1, 2, 4, 8, etc), aligned to that same power-of-2
+/// memory address.
+///
+/// If a user asks to watch 4 bytes at address 0x1002 (0x1002-0x1005
+/// inclusive) we can implement this with two 2-byte watchpoints
+/// (0x1002 and 0x1004) or with an 8-byte watchpoint at 0x1000.
+/// A 4-byte watchpoint at 0x1002 would not be properly 4 byte aligned.
+///
+/// If a user asks to watch 16 bytes at 0x1000, and this target supports
+/// 8-byte watchpoints, we can implement this with two 8-byte watchpoints
+/// at 0x1000 and 0x1008.
+std::vector<WatchpointAlgorithms::Region>
+WatchpointAlgorithms::PowerOf2Watchpoints(addr_t user_addr, size_t user_size,
+ size_t min_byte_size,
+ size_t max_byte_size,
+ uint32_t address_byte_size) {
+
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOGV(log,
+ "AtomizeWatchpointRequest user request addr {0:x} size {1} "
+ "min_byte_size {2}, max_byte_size {3}, address_byte_size {4}",
+ user_addr, user_size, min_byte_size, max_byte_size,
+ address_byte_size);
+
+ // Can't watch zero bytes.
+ if (user_size == 0)
+ return {};
+
+ size_t aligned_size = std::max(user_size, min_byte_size);
+ /// Round up \a user_size to the next power-of-2 size
+ /// user_size == 8 -> aligned_size == 8
+ /// user_size == 9 -> aligned_size == 16
+ aligned_size = bit_ceil(aligned_size);
+
+ addr_t aligned_start = user_addr & ~(aligned_size - 1);
+
+ // Does this power-of-2 memory range, aligned to power-of-2 that the
+ // hardware can watch, completely cover the requested region.
+ if (aligned_size <= max_byte_size &&
+ aligned_start + aligned_size >= user_addr + user_size)
+ return {{aligned_start, aligned_size}};
+
+ // If the maximum region we can watch is larger than the aligned
+ // size, try increasing the region size by one power of 2 and see
+ // if aligning to that amount can cover the requested region.
+ //
+ // Increasing the aligned_size repeatedly instead of splitting the
+ // watchpoint can result in us watching large regions of memory
+ // unintentionally when we could use small two watchpoints. e.g.
+ // user_addr 0x3ff8 user_size 32
+ // can be watched with four 8-byte watchpoints or if it's done with one
+ // MASK watchpoint, it would need to be a 32KB watchpoint (a 16KB
+ // watchpoint at 0x0 only covers 0x0000-0x4000). A user request
+ // at the end of a power-of-2 region can lead to these undesirably
+ // large watchpoints and many false positive hits to ignore.
+ if (max_byte_size >= (aligned_size << 1)) {
+ aligned_size <<= 1;
+ aligned_start = user_addr & ~(aligned_size - 1);
+ if (aligned_size <= max_byte_size &&
+ aligned_start + aligned_size >= user_addr + user_size)
+ return {{aligned_start, aligned_size}};
+
+ // Go back to our original aligned size, to try the multiple
+ // watchpoint approach.
+ aligned_size >>= 1;
+ }
+
+ // We need to split the user's watchpoint into two or more watchpoints
+ // that can be monitored by hardware, because of alignment and/or size
+ // reasons.
+ aligned_size = std::min(aligned_size, max_byte_size);
+ aligned_start = user_addr & ~(aligned_size - 1);
+
+ std::vector<Region> result;
+ addr_t current_address = aligned_start;
+ const addr_t user_end_address = user_addr + user_size;
+ while (current_address + aligned_size < user_end_address) {
+ result.push_back({current_address, aligned_size});
+ current_address += aligned_size;
+ }
+
+ if (current_address < user_end_address)
+ result.push_back({current_address, aligned_size});
+
+ return result;
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp
new file mode 100644
index 000000000000..f7564483e6f1
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointList.cpp
@@ -0,0 +1,254 @@
+//===-- WatchpointList.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/WatchpointList.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+WatchpointList::WatchpointList() = default;
+
+WatchpointList::~WatchpointList() = default;
+
+// Add a watchpoint to the list.
+lldb::watch_id_t WatchpointList::Add(const WatchpointSP &wp_sp, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_sp->SetID(++m_next_wp_id);
+ m_watchpoints.push_back(wp_sp);
+ if (notify) {
+ if (wp_sp->GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitWatchpointChanged)) {
+ auto data_sp = std::make_shared<Watchpoint::WatchpointEventData>(
+ eWatchpointEventTypeAdded, wp_sp);
+ wp_sp->GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged,
+ data_sp);
+ }
+ }
+ return wp_sp->GetID();
+}
+
+void WatchpointList::Dump(Stream *s) const {
+ DumpWithLevel(s, lldb::eDescriptionLevelBrief);
+}
+
+void WatchpointList::DumpWithLevel(
+ Stream *s, lldb::DescriptionLevel description_level) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ s->Printf("%p: ", static_cast<const void *>(this));
+ // s->Indent();
+ s->Printf("WatchpointList with %" PRIu64 " Watchpoints:\n",
+ (uint64_t)m_watchpoints.size());
+ s->IndentMore();
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ (*pos)->DumpWithLevel(s, description_level);
+ s->IndentLess();
+}
+
+const WatchpointSP WatchpointList::FindByAddress(lldb::addr_t addr) const {
+ WatchpointSP wp_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_watchpoints.empty()) {
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos) {
+ lldb::addr_t wp_addr = (*pos)->GetLoadAddress();
+ uint32_t wp_bytesize = (*pos)->GetByteSize();
+ if ((wp_addr <= addr) && ((wp_addr + wp_bytesize) > addr)) {
+ wp_sp = *pos;
+ break;
+ }
+ }
+ }
+
+ return wp_sp;
+}
+
+const WatchpointSP WatchpointList::FindBySpec(std::string spec) const {
+ WatchpointSP wp_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (!m_watchpoints.empty()) {
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ if ((*pos)->GetWatchSpec() == spec) {
+ wp_sp = *pos;
+ break;
+ }
+ }
+
+ return wp_sp;
+}
+
+class WatchpointIDMatches {
+public:
+ WatchpointIDMatches(lldb::watch_id_t watch_id) : m_watch_id(watch_id) {}
+
+ bool operator()(const WatchpointSP &wp) const {
+ return m_watch_id == wp->GetID();
+ }
+
+private:
+ const lldb::watch_id_t m_watch_id;
+};
+
+WatchpointList::wp_collection::iterator
+WatchpointList::GetIDIterator(lldb::watch_id_t watch_id) {
+ return std::find_if(m_watchpoints.begin(),
+ m_watchpoints.end(), // Search full range
+ WatchpointIDMatches(watch_id)); // Predicate
+}
+
+WatchpointList::wp_collection::const_iterator
+WatchpointList::GetIDConstIterator(lldb::watch_id_t watch_id) const {
+ return std::find_if(m_watchpoints.begin(),
+ m_watchpoints.end(), // Search full range
+ WatchpointIDMatches(watch_id)); // Predicate
+}
+
+WatchpointSP WatchpointList::FindByID(lldb::watch_id_t watch_id) const {
+ WatchpointSP wp_sp;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::const_iterator pos = GetIDConstIterator(watch_id);
+ if (pos != m_watchpoints.end())
+ wp_sp = *pos;
+
+ return wp_sp;
+}
+
+lldb::watch_id_t WatchpointList::FindIDByAddress(lldb::addr_t addr) {
+ WatchpointSP wp_sp = FindByAddress(addr);
+ if (wp_sp) {
+ return wp_sp->GetID();
+ }
+ return LLDB_INVALID_WATCH_ID;
+}
+
+lldb::watch_id_t WatchpointList::FindIDBySpec(std::string spec) {
+ WatchpointSP wp_sp = FindBySpec(spec);
+ if (wp_sp) {
+ return wp_sp->GetID();
+ }
+ return LLDB_INVALID_WATCH_ID;
+}
+
+WatchpointSP WatchpointList::GetByIndex(uint32_t i) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ WatchpointSP wp_sp;
+ if (i < m_watchpoints.size()) {
+ wp_collection::const_iterator pos = m_watchpoints.begin();
+ std::advance(pos, i);
+ wp_sp = *pos;
+ }
+ return wp_sp;
+}
+
+const WatchpointSP WatchpointList::GetByIndex(uint32_t i) const {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ WatchpointSP wp_sp;
+ if (i < m_watchpoints.size()) {
+ wp_collection::const_iterator pos = m_watchpoints.begin();
+ std::advance(pos, i);
+ wp_sp = *pos;
+ }
+ return wp_sp;
+}
+
+std::vector<lldb::watch_id_t> WatchpointList::GetWatchpointIDs() const {
+ std::vector<lldb::watch_id_t> IDs;
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ IDs.push_back((*pos)->GetID());
+ return IDs;
+}
+
+bool WatchpointList::Remove(lldb::watch_id_t watch_id, bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::iterator pos = GetIDIterator(watch_id);
+ if (pos != m_watchpoints.end()) {
+ WatchpointSP wp_sp = *pos;
+ if (notify) {
+ if (wp_sp->GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitWatchpointChanged)) {
+ auto data_sp = std::make_shared<Watchpoint::WatchpointEventData>(
+ eWatchpointEventTypeRemoved, wp_sp);
+ wp_sp->GetTarget().BroadcastEvent(
+ Target::eBroadcastBitWatchpointChanged, data_sp);
+ }
+ }
+ m_watchpoints.erase(pos);
+ return true;
+ }
+ return false;
+}
+
+uint32_t WatchpointList::GetHitCount() const {
+ uint32_t hit_count = 0;
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::const_iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ hit_count += (*pos)->GetHitCount();
+ return hit_count;
+}
+
+bool WatchpointList::ShouldStop(StoppointCallbackContext *context,
+ lldb::watch_id_t watch_id) {
+
+ WatchpointSP wp_sp = FindByID(watch_id);
+ if (wp_sp) {
+ // Let the Watchpoint decide if it should stop here (could not have reached
+ // it's target hit count yet, or it could have a callback that decided it
+ // shouldn't stop.
+ return wp_sp->ShouldStop(context);
+ }
+ // We should stop here since this Watchpoint isn't valid anymore or it
+ // doesn't exist.
+ return true;
+}
+
+void WatchpointList::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ wp_collection::iterator pos, end = m_watchpoints.end();
+
+ for (pos = m_watchpoints.begin(); pos != end; ++pos) {
+ s->Printf(" ");
+ (*pos)->Dump(s);
+ }
+}
+
+void WatchpointList::SetEnabledAll(bool enabled) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+
+ wp_collection::iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos)
+ (*pos)->SetEnabled(enabled);
+}
+
+void WatchpointList::RemoveAll(bool notify) {
+ std::lock_guard<std::recursive_mutex> guard(m_mutex);
+ if (notify) {
+
+ {
+ wp_collection::iterator pos, end = m_watchpoints.end();
+ for (pos = m_watchpoints.begin(); pos != end; ++pos) {
+ if ((*pos)->GetTarget().EventTypeHasListeners(
+ Target::eBroadcastBitBreakpointChanged)) {
+ auto data_sp = std::make_shared<Watchpoint::WatchpointEventData>(
+ eWatchpointEventTypeRemoved, *pos);
+ (*pos)->GetTarget().BroadcastEvent(
+ Target::eBroadcastBitWatchpointChanged, data_sp);
+ }
+ }
+ }
+ }
+ m_watchpoints.clear();
+}
+
+void WatchpointList::GetListMutex(
+ std::unique_lock<std::recursive_mutex> &lock) {
+ lock = std::unique_lock<std::recursive_mutex>(m_mutex);
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp
new file mode 100644
index 000000000000..c27cd62e0220
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointOptions.cpp
@@ -0,0 +1,180 @@
+//===-- WatchpointOptions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Breakpoint/WatchpointOptions.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Utility/Stream.h"
+#include "lldb/Utility/StringList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+bool WatchpointOptions::NullCallback(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t watch_id) {
+ return true;
+}
+
+// WatchpointOptions constructor
+WatchpointOptions::WatchpointOptions()
+ : m_callback(WatchpointOptions::NullCallback) {}
+
+// WatchpointOptions copy constructor
+WatchpointOptions::WatchpointOptions(const WatchpointOptions &rhs)
+ : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
+ m_callback_is_synchronous(rhs.m_callback_is_synchronous) {
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
+}
+
+// WatchpointOptions assignment operator
+const WatchpointOptions &WatchpointOptions::
+operator=(const WatchpointOptions &rhs) {
+ m_callback = rhs.m_callback;
+ m_callback_baton_sp = rhs.m_callback_baton_sp;
+ m_callback_is_synchronous = rhs.m_callback_is_synchronous;
+ if (rhs.m_thread_spec_up != nullptr)
+ m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
+ return *this;
+}
+
+WatchpointOptions *
+WatchpointOptions::CopyOptionsNoCallback(WatchpointOptions &orig) {
+ WatchpointHitCallback orig_callback = orig.m_callback;
+ lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp;
+ bool orig_is_sync = orig.m_callback_is_synchronous;
+
+ orig.ClearCallback();
+ WatchpointOptions *ret_val = new WatchpointOptions(orig);
+
+ orig.SetCallback(orig_callback, orig_callback_baton_sp, orig_is_sync);
+
+ return ret_val;
+}
+
+// Destructor
+WatchpointOptions::~WatchpointOptions() = default;
+
+// Callbacks
+void WatchpointOptions::SetCallback(WatchpointHitCallback callback,
+ const BatonSP &callback_baton_sp,
+ bool callback_is_synchronous) {
+ m_callback_is_synchronous = callback_is_synchronous;
+ m_callback = callback;
+ m_callback_baton_sp = callback_baton_sp;
+}
+
+void WatchpointOptions::ClearCallback() {
+ m_callback = WatchpointOptions::NullCallback;
+ m_callback_is_synchronous = false;
+ m_callback_baton_sp.reset();
+}
+
+Baton *WatchpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
+
+const Baton *WatchpointOptions::GetBaton() const {
+ return m_callback_baton_sp.get();
+}
+
+bool WatchpointOptions::InvokeCallback(StoppointCallbackContext *context,
+ lldb::user_id_t watch_id) {
+ if (m_callback && context->is_synchronous == IsCallbackSynchronous()) {
+ return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
+ : nullptr,
+ context, watch_id);
+ } else
+ return true;
+}
+
+bool WatchpointOptions::HasCallback() {
+ return m_callback != WatchpointOptions::NullCallback;
+}
+
+const ThreadSpec *WatchpointOptions::GetThreadSpecNoCreate() const {
+ return m_thread_spec_up.get();
+}
+
+ThreadSpec *WatchpointOptions::GetThreadSpec() {
+ if (m_thread_spec_up == nullptr)
+ m_thread_spec_up = std::make_unique<ThreadSpec>();
+
+ return m_thread_spec_up.get();
+}
+
+void WatchpointOptions::SetThreadID(lldb::tid_t thread_id) {
+ GetThreadSpec()->SetTID(thread_id);
+}
+
+void WatchpointOptions::GetCallbackDescription(
+ Stream *s, lldb::DescriptionLevel level) const {
+ if (m_callback_baton_sp.get()) {
+ s->EOL();
+ m_callback_baton_sp->GetDescription(s->AsRawOstream(), level,
+ s->GetIndentLevel());
+ }
+}
+
+void WatchpointOptions::GetDescription(Stream *s,
+ lldb::DescriptionLevel level) const {
+ // Figure out if there are any options not at their default value, and only
+ // print anything if there are:
+
+ if ((GetThreadSpecNoCreate() != nullptr &&
+ GetThreadSpecNoCreate()->HasSpecification())) {
+ if (level == lldb::eDescriptionLevelVerbose) {
+ s->EOL();
+ s->IndentMore();
+ s->Indent();
+ s->PutCString("Watchpoint Options:\n");
+ s->IndentMore();
+ s->Indent();
+ } else
+ s->PutCString(" Options: ");
+
+ if (m_thread_spec_up)
+ m_thread_spec_up->GetDescription(s, level);
+ else if (level == eDescriptionLevelBrief)
+ s->PutCString("thread spec: no ");
+ if (level == lldb::eDescriptionLevelFull) {
+ s->IndentLess();
+ s->IndentMore();
+ }
+ }
+
+ GetCallbackDescription(s, level);
+}
+
+void WatchpointOptions::CommandBaton::GetDescription(
+ llvm::raw_ostream &s, lldb::DescriptionLevel level,
+ unsigned indentation) const {
+ const CommandData *data = getItem();
+
+ if (level == eDescriptionLevelBrief) {
+ s << ", commands = %s"
+ << ((data && data->user_source.GetSize() > 0) ? "yes" : "no");
+ return;
+ }
+
+ indentation += 2;
+ s.indent(indentation);
+ s << "watchpoint commands:\n";
+
+ indentation += 2;
+ if (data && data->user_source.GetSize() > 0) {
+ for (const std::string &line : data->user_source) {
+ s.indent(indentation);
+ s << line << "\n";
+ }
+ } else
+ s << "No commands.\n";
+}
diff --git a/contrib/llvm-project/lldb/source/Breakpoint/WatchpointResource.cpp b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointResource.cpp
new file mode 100644
index 000000000000..fa0442997b52
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Breakpoint/WatchpointResource.cpp
@@ -0,0 +1,123 @@
+//===-- WatchpointResource.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 <assert.h>
+
+#include "lldb/Breakpoint/WatchpointResource.h"
+#include "lldb/Utility/Stream.h"
+
+#include <algorithm>
+
+using namespace lldb;
+using namespace lldb_private;
+
+WatchpointResource::WatchpointResource(lldb::addr_t addr, size_t size,
+ bool read, bool write)
+ : m_id(GetNextID()), m_addr(addr), m_size(size),
+ m_watch_read(read), m_watch_write(write) {}
+
+WatchpointResource::~WatchpointResource() {
+ std::lock_guard<std::mutex> guard(m_constituents_mutex);
+ m_constituents.clear();
+}
+
+addr_t WatchpointResource::GetLoadAddress() const { return m_addr; }
+
+size_t WatchpointResource::GetByteSize() const { return m_size; }
+
+bool WatchpointResource::WatchpointResourceRead() const { return m_watch_read; }
+
+bool WatchpointResource::WatchpointResourceWrite() const {
+ return m_watch_write;
+}
+
+void WatchpointResource::SetType(bool read, bool write) {
+ m_watch_read = read;
+ m_watch_write = write;
+}
+
+wp_resource_id_t WatchpointResource::GetID() const { return m_id; }
+
+bool WatchpointResource::Contains(addr_t addr) {
+ if (addr >= m_addr && addr < m_addr + m_size)
+ return true;
+ return false;
+}
+
+void WatchpointResource::AddConstituent(const WatchpointSP &wp_sp) {
+ std::lock_guard<std::mutex> guard(m_constituents_mutex);
+ m_constituents.push_back(wp_sp);
+}
+
+void WatchpointResource::RemoveConstituent(WatchpointSP &wp_sp) {
+ std::lock_guard<std::mutex> guard(m_constituents_mutex);
+ const auto &it =
+ std::find(m_constituents.begin(), m_constituents.end(), wp_sp);
+ if (it != m_constituents.end())
+ m_constituents.erase(it);
+}
+
+size_t WatchpointResource::GetNumberOfConstituents() {
+ std::lock_guard<std::mutex> guard(m_constituents_mutex);
+ return m_constituents.size();
+}
+
+bool WatchpointResource::ConstituentsContains(const WatchpointSP &wp_sp) {
+ return ConstituentsContains(wp_sp.get());
+}
+
+bool WatchpointResource::ConstituentsContains(const Watchpoint *wp) {
+ std::lock_guard<std::mutex> guard(m_constituents_mutex);
+ WatchpointCollection::const_iterator match =
+ std::find_if(m_constituents.begin(), m_constituents.end(),
+ [&wp](const WatchpointSP &x) { return x.get() == wp; });
+ return match != m_constituents.end();
+}
+
+WatchpointSP WatchpointResource::GetConstituentAtIndex(size_t idx) {
+ std::lock_guard<std::mutex> guard(m_constituents_mutex);
+ assert(idx < m_constituents.size());
+ if (idx >= m_constituents.size())
+ return {};
+
+ return m_constituents[idx];
+}
+
+WatchpointResource::WatchpointCollection
+WatchpointResource::CopyConstituentsList() {
+ std::lock_guard<std::mutex> guard(m_constituents_mutex);
+ return m_constituents;
+}
+
+bool WatchpointResource::ShouldStop(StoppointCallbackContext *context) {
+ // LWP_TODO: Need to poll all Watchpoint constituents and see if
+ // we should stop, like BreakpointSites do.
+#if 0
+ m_hit_counter.Increment();
+ // ShouldStop can do a lot of work, and might even come back and hit
+ // this breakpoint site again. So don't hold the m_constituents_mutex the
+ // whole while. Instead make a local copy of the collection and call
+ // ShouldStop on the copy.
+ WatchpointResourceCollection constituents_copy;
+ {
+ std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
+ constituents_copy = m_constituents;
+ }
+ return constituents_copy.ShouldStop(context);
+#endif
+ return true;
+}
+
+void WatchpointResource::Dump(Stream *s) const {
+ s->Printf("addr = 0x%8.8" PRIx64 " size = %zu", m_addr, m_size);
+}
+
+wp_resource_id_t WatchpointResource::GetNextID() {
+ static wp_resource_id_t g_next_id = 0;
+ return ++g_next_id;
+}