diff options
author | 2020-08-03 14:33:06 +0000 | |
---|---|---|
committer | 2020-08-03 14:33:06 +0000 | |
commit | 061da546b983eb767bad15e67af1174fb0bcf31c (patch) | |
tree | 83c78b820819d70aa40c36d90447978b300078c5 /gnu/llvm/lldb/source/Breakpoint | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.tar.xz wireguard-openbsd-061da546b983eb767bad15e67af1174fb0bcf31c.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/lldb/source/Breakpoint')
25 files changed, 6630 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/source/Breakpoint/Breakpoint.cpp b/gnu/llvm/lldb/source/Breakpoint/Breakpoint.cpp new file mode 100644 index 00000000000..13acf4bb92e --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/Breakpoint.cpp @@ -0,0 +1,1102 @@ +//===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===// +// +// 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/Target/SectionLoadList.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.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; + +ConstString Breakpoint::GetEventIdentifier() { + static ConstString g_identifier("event-identifier.breakpoint.changed"); + return g_identifier; +} + +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_being_created(true), m_hardware(hardware), m_target(target), + m_filter_sp(filter_sp), m_resolver_sp(resolver_sp), + m_options_up(new BreakpointOptions(true)), m_locations(*this), + m_resolve_indirect_symbols(resolve_indirect_symbols), m_hit_count(0) { + m_being_created = false; +} + +Breakpoint::Breakpoint(Target &new_target, Breakpoint &source_bp) + : m_being_created(true), m_hardware(source_bp.m_hardware), + m_target(new_target), m_name_list(source_bp.m_name_list), + m_options_up(new BreakpointOptions(*source_bp.m_options_up)), + m_locations(*this), + m_resolve_indirect_symbols(source_bp.m_resolve_indirect_symbols), + m_hit_count(0) { + // Now go through and copy the filter & resolver: + m_resolver_sp = source_bp.m_resolver_sp->CopyForBreakpoint(*this); + m_filter_sp = source_bp.m_filter_sp->CopyForBreakpoint(*this); +} + +// Destructor +Breakpoint::~Breakpoint() = default; + +// 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_up->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( + Target &target, StructuredData::ObjectSP &object_data, Status &error) { + BreakpointSP 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.SetErrorStringWithFormat( + "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.shared_from_this()); + else { + filter_sp = SearchFilter::CreateFromStructuredData(target, *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; + 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_up = std::move(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++) { + llvm::StringRef name; + Status error; + success = names_array->GetItemAtIndexAsString(i, name); + target.AddNameToBreakpoint(result_sp, name.str().c_str(), 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(); + std::vector<std::string>::iterator begin = names.begin(); + std::vector<std::string>::iterator end = names.end(); + + for (size_t i = 0; i < num_names; i++) { + llvm::StringRef name; + if (names_array->GetItemAtIndexAsString(i, name)) { + if (std::find(begin, end, name) != end) { + 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_up->IsEnabled()) + return; + + m_options_up->SetEnabled(enable); + if (enable) + m_locations.ResolveAllBreakpointSites(); + else + m_locations.ClearAllBreakpointSites(); + + SendBreakpointChangedEvent(enable ? eBreakpointEventTypeEnabled + : eBreakpointEventTypeDisabled); +} + +bool Breakpoint::IsEnabled() { return m_options_up->IsEnabled(); } + +void Breakpoint::SetIgnoreCount(uint32_t n) { + if (m_options_up->GetIgnoreCount() == n) + return; + + m_options_up->SetIgnoreCount(n); + SendBreakpointChangedEvent(eBreakpointEventTypeIgnoreChanged); +} + +void Breakpoint::DecrementIgnoreCount() { + uint32_t ignore = m_options_up->GetIgnoreCount(); + if (ignore != 0) + m_options_up->SetIgnoreCount(ignore - 1); +} + +uint32_t Breakpoint::GetIgnoreCount() const { + return m_options_up->GetIgnoreCount(); +} + +bool Breakpoint::IgnoreCountShouldStop() { + uint32_t ignore = GetIgnoreCount(); + if (ignore != 0) { + // When we get here we know the location that caused the stop doesn't have + // an ignore count, since by contract we call it first... So we don't have + // to find & decrement it, we only have to decrement our own ignore count. + DecrementIgnoreCount(); + return false; + } else + return true; +} + +uint32_t Breakpoint::GetHitCount() const { return m_hit_count; } + +bool Breakpoint::IsOneShot() const { return m_options_up->IsOneShot(); } + +void Breakpoint::SetOneShot(bool one_shot) { + m_options_up->SetOneShot(one_shot); +} + +bool Breakpoint::IsAutoContinue() const { + return m_options_up->IsAutoContinue(); +} + +void Breakpoint::SetAutoContinue(bool auto_continue) { + m_options_up->SetAutoContinue(auto_continue); +} + +void Breakpoint::SetThreadID(lldb::tid_t thread_id) { + if (m_options_up->GetThreadSpec()->GetTID() == thread_id) + return; + + m_options_up->GetThreadSpec()->SetTID(thread_id); + SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t Breakpoint::GetThreadID() const { + if (m_options_up->GetThreadSpecNoCreate() == nullptr) + return LLDB_INVALID_THREAD_ID; + else + return m_options_up->GetThreadSpecNoCreate()->GetTID(); +} + +void Breakpoint::SetThreadIndex(uint32_t index) { + if (m_options_up->GetThreadSpec()->GetIndex() == index) + return; + + m_options_up->GetThreadSpec()->SetIndex(index); + SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); +} + +uint32_t Breakpoint::GetThreadIndex() const { + if (m_options_up->GetThreadSpecNoCreate() == nullptr) + return 0; + else + return m_options_up->GetThreadSpecNoCreate()->GetIndex(); +} + +void Breakpoint::SetThreadName(const char *thread_name) { + if (m_options_up->GetThreadSpec()->GetName() != nullptr && + ::strcmp(m_options_up->GetThreadSpec()->GetName(), thread_name) == 0) + return; + + m_options_up->GetThreadSpec()->SetName(thread_name); + SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); +} + +const char *Breakpoint::GetThreadName() const { + if (m_options_up->GetThreadSpecNoCreate() == nullptr) + return nullptr; + else + return m_options_up->GetThreadSpecNoCreate()->GetName(); +} + +void Breakpoint::SetQueueName(const char *queue_name) { + if (m_options_up->GetThreadSpec()->GetQueueName() != nullptr && + ::strcmp(m_options_up->GetThreadSpec()->GetQueueName(), queue_name) == 0) + return; + + m_options_up->GetThreadSpec()->SetQueueName(queue_name); + SendBreakpointChangedEvent(eBreakpointEventTypeThreadChanged); +} + +const char *Breakpoint::GetQueueName() const { + if (m_options_up->GetThreadSpecNoCreate() == nullptr) + return nullptr; + else + return m_options_up->GetThreadSpecNoCreate()->GetQueueName(); +} + +void Breakpoint::SetCondition(const char *condition) { + m_options_up->SetCondition(condition); + SendBreakpointChangedEvent(eBreakpointEventTypeConditionChanged); +} + +const char *Breakpoint::GetConditionText() const { + return m_options_up->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 goes out of scope. + m_options_up->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_up->SetCallback(callback, callback_baton_sp, is_synchronous); +} + +void Breakpoint::ClearCallback() { m_options_up->ClearCallback(); } + +bool Breakpoint::InvokeCallback(StoppointCallbackContext *context, + break_id_t bp_loc_id) { + return m_options_up->InvokeCallback(context, GetID(), bp_loc_id); +} + +BreakpointOptions *Breakpoint::GetOptions() { return m_options_up.get(); } + +const BreakpointOptions *Breakpoint::GetOptions() const { + return m_options_up.get(); +} + +void Breakpoint::ResolveBreakpoint() { + if (m_resolver_sp) + m_resolver_sp->ResolveBreakpoint(*m_filter_sp); +} + +void Breakpoint::ResolveBreakpointInModules( + ModuleList &module_list, BreakpointLocationCollection &new_locations) { + 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) { + BreakpointEventData *new_locations_event = new BreakpointEventData( + eBreakpointEventTypeLocationsAdded, shared_from_this()); + + ResolveBreakpointInModules( + module_list, new_locations_event->GetBreakpointLocationCollection()); + + if (new_locations_event->GetBreakpointLocationCollection().GetSize() != + 0) { + SendBreakpointChangedEvent(new_locations_event); + } else + delete new_locations_event; + } else { + 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, + "Breakpoint::ModulesChanged: num_modules: %zu load: %i " + "delete_locations: %i\n", + module_list.GetSize(), load, delete_locations); + + std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex()); + 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.ModulesNoLocking()) { + 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. + + BreakpointEventData *removed_locations_event; + if (!IsInternal()) + removed_locations_event = new BreakpointEventData( + eBreakpointEventTypeLocationsRemoved, shared_from_this()); + else + removed_locations_event = nullptr; + + size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; i++) { + ModuleSP module_sp(module_list.GetModuleAtIndexUnlocked(i)); + 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); + } +} + +namespace { +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; +} +} // anonymous namespace + +void Breakpoint::ModuleReplaced(ModuleSP old_module_sp, + ModuleSP new_module_sp) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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. + + BreakpointEventData *locations_event; + if (!IsInternal()) + locations_event = new BreakpointEventData( + eBreakpointEventTypeLocationsRemoved, shared_from_this()); + else + locations_event = nullptr; + + for (BreakpointLocationSP loc_sp : + locations_to_remove.BreakpointLocations()) { + m_locations.RemoveLocation(loc_sp); + if (locations_event) + locations_event->GetBreakpointLocationCollection().Add(loc_sp); + } + SendBreakpointChangedEvent(locations_event); + + // And announce the new ones. + + if (!IsInternal()) { + locations_event = new BreakpointEventData( + eBreakpointEventTypeLocationsAdded, shared_from_this()); + for (BreakpointLocationSP loc_sp : + locations_to_announce.BreakpointLocations()) + locations_event->GetBreakpointLocationCollection().Add(loc_sp); + + SendBreakpointChangedEvent(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(); } + +bool Breakpoint::AddName(llvm::StringRef new_name) { + m_name_list.insert(new_name.str().c_str()); + return true; +} + +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)"); + } + + GetOptions()->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 (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(); + GetOptions()->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()); + if (resolverFileLine && + resolverFileLine->m_file_spec.GetFilename() == filename && + resolverFileLine->m_line_number == 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 (!m_being_created && !IsInternal() && + GetTarget().EventTypeHasListeners( + Target::eBroadcastBitBreakpointChanged)) { + BreakpointEventData *data = + new Breakpoint::BreakpointEventData(eventKind, shared_from_this()); + + GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data); + } +} + +void Breakpoint::SendBreakpointChangedEvent(BreakpointEventData *data) { + if (data == nullptr) + return; + + if (!m_being_created && !IsInternal() && + GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, data); + else + delete data; +} + +Breakpoint::BreakpointEventData::BreakpointEventData( + BreakpointEventType sub_type, const BreakpointSP &new_breakpoint_sp) + : EventData(), m_breakpoint_event(sub_type), + m_new_breakpoint_sp(new_breakpoint_sp) {} + +Breakpoint::BreakpointEventData::~BreakpointEventData() = default; + +ConstString Breakpoint::BreakpointEventData::GetFlavorString() { + static ConstString g_flavor("Breakpoint::BreakpointEventData"); + return g_flavor; +} + +ConstString Breakpoint::BreakpointEventData::GetFlavor() const { + return BreakpointEventData::GetFlavorString(); +} + +BreakpointSP &Breakpoint::BreakpointEventData::GetBreakpoint() { + return m_new_breakpoint_sp; +} + +BreakpointEventType +Breakpoint::BreakpointEventData::GetBreakpointEventType() const { + return m_breakpoint_event; +} + +void Breakpoint::BreakpointEventData::Dump(Stream *s) const {} + +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; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointID.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointID.cpp new file mode 100644 index 00000000000..dc2e57cb085 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointID.cpp @@ -0,0 +1,121 @@ +//===-- BreakpointID.cpp ----------------------------------------*- C++ -*-===// +// +// 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 <stdio.h> + +#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) { + for (auto spec : g_range_specifiers) { + if (spec == str) + return true; + } + + return false; +} + +bool BreakpointID::IsValidIDExpression(llvm::StringRef str) { + return BreakpointID::ParseCanonicalReference(str).hasValue(); +} + +llvm::ArrayRef<llvm::StringRef> BreakpointID::GetRangeSpecifiers() { + return llvm::makeArrayRef(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); +} + +llvm::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 llvm::None; + + // If it doesn't start with an integer, it's not valid. + if (input.consumeInteger(0, bp_id)) + return llvm::None; + + // 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 llvm::None; + } + + // And at the end, the entire string must have been consumed. + if (!input.empty()) + return llvm::None; + + return BreakpointID(bp_id, loc_id); +} + +bool BreakpointID::StringIsBreakpointName(llvm::StringRef str, Status &error) { + error.Clear(); + if (str.empty()) + { + error.SetErrorStringWithFormat("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 '-': \"%s\"", + str.str().c_str()); + return false; + } + + return true; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointIDList.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointIDList.cpp new file mode 100644 index 00000000000..de68c44ec6a --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointIDList.cpp @@ -0,0 +1,348 @@ +//===-- BreakpointIDList.cpp ------------------------------------*- C++ -*-===// +// +// 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/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Args.h" + +using namespace lldb; +using namespace lldb_private; + +// class BreakpointIDList + +BreakpointIDList::BreakpointIDList() + : m_invalid_id(LLDB_INVALID_BREAK_ID, LLDB_INVALID_BREAK_ID) {} + +BreakpointIDList::~BreakpointIDList() = default; + +size_t BreakpointIDList::GetSize() const { return m_breakpoint_ids.size(); } + +const BreakpointID & +BreakpointIDList::GetBreakpointIDAtIndex(size_t index) const { + return ((index < m_breakpoint_ids.size()) ? m_breakpoint_ids[index] + : m_invalid_id); +} + +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::AddBreakpointID(const char *bp_id_str) { + auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str); + if (!bp_id.hasValue()) + return false; + + m_breakpoint_ids.push_back(*bp_id); + return true; +} + +bool BreakpointIDList::FindBreakpointID(BreakpointID &bp_id, + size_t *position) const { + for (size_t i = 0; i < m_breakpoint_ids.size(); ++i) { + BreakpointID tmp_id = m_breakpoint_ids[i]; + if (tmp_id.GetBreakpointID() == bp_id.GetBreakpointID() && + tmp_id.GetLocationID() == bp_id.GetLocationID()) { + *position = i; + return true; + } + } + + return false; +} + +bool BreakpointIDList::FindBreakpointID(const char *bp_id_str, + size_t *position) const { + auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str); + if (!bp_id.hasValue()) + return false; + + return FindBreakpointID(*bp_id, position); +} + +void BreakpointIDList::InsertStringArray( + llvm::ArrayRef<const char *> string_array, CommandReturnObject &result) { + if(string_array.empty()) + return; + + for (const char *str : string_array) { + auto bp_id = BreakpointID::ParseCanonicalReference(str); + if (bp_id.hasValue()) + m_breakpoint_ids.push_back(*bp_id); + } + result.SetStatus(eReturnStatusSuccessFinishNoResult); +} + +// 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. + +void BreakpointIDList::FindAndReplaceIDRanges(Args &old_args, Target *target, + bool allow_locations, + BreakpointName::Permissions + ::PermissionKinds purpose, + CommandReturnObject &result, + 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('.')) { + result.AppendErrorWithFormat( + "Breakpoint locations not allowed, saw location: %s.", + current_arg.str().c_str()); + new_args.Clear(); + return; + } + + 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(); + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return; + } else + names_found.insert(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(); + size_t pos = tmp_str.find('.'); + if (pos != llvm::StringRef::npos) { + llvm::StringRef bp_id_str = tmp_str.substr(0, pos); + if (BreakpointID::IsValidIDExpression(bp_id_str) && + tmp_str[pos + 1] == '*' && tmp_str.size() == (pos + 2)) { + + BreakpointSP breakpoint_sp; + auto bp_id = BreakpointID::ParseCanonicalReference(bp_id_str); + if (bp_id.hasValue()) + breakpoint_sp = target->GetBreakpointByID(bp_id->GetBreakpointID()); + if (!breakpoint_sp) { + new_args.Clear(); + result.AppendErrorWithFormat("'%d' is not a valid breakpoint ID.\n", + bp_id->GetBreakpointID()); + result.SetStatus(eReturnStatusFailed); + return; + } + 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.hasValue() || + !target->GetBreakpointByID(start_bp->GetBreakpointID())) { + new_args.Clear(); + result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n", + range_from.str().c_str()); + result.SetStatus(eReturnStatusFailed); + return; + } + + if (!end_bp.hasValue() || + !target->GetBreakpointByID(end_bp->GetBreakpointID())) { + new_args.Clear(); + result.AppendErrorWithFormat("'%s' is not a valid breakpoint ID.\n", + range_to.str().c_str()); + result.SetStatus(eReturnStatusFailed); + return; + } + 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(); + result.AppendErrorWithFormat("Invalid breakpoint id range: Either " + "both ends of range must specify" + " a breakpoint location, or neither can " + "specify a breakpoint location.\n"); + result.SetStatus(eReturnStatusFailed); + return; + } + + // 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(); + result.AppendErrorWithFormat( + "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); + result.SetStatus(eReturnStatusFailed); + return; + } + } + + 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 (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()); + } + } + } + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); +} + +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/gnu/llvm/lldb/source/Breakpoint/BreakpointList.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointList.cpp new file mode 100644 index 00000000000..5b23c633d14 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointList.cpp @@ -0,0 +1,195 @@ +//===-- BreakpointList.cpp --------------------------------------*- C++ -*-===// +// +// 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)) + target.BroadcastEvent(Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData(event, bp)); +} + +BreakpointList::BreakpointList(bool is_internal) + : m_mutex(), m_breakpoints(), m_next_break_id(0), + m_is_internal(is_internal) {} + +BreakpointList::~BreakpointList() {} + +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); + } + + m_breakpoints.erase( + std::remove_if(m_breakpoints.begin(), m_breakpoints.end(), + [&](const BreakpointSP &bp) { return bp->AllowDelete(); }), + m_breakpoints.end()); +} + +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::GetListMutex( + std::unique_lock<std::recursive_mutex> &lock) { + lock = std::unique_lock<std::recursive_mutex>(m_mutex); +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointLocation.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointLocation.cpp new file mode 100644 index 00000000000..7f08b08c605 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -0,0 +1,659 @@ +//===-- BreakpointLocation.cpp ----------------------------------*- C++ -*-===// +// +// 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/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) + : StoppointLocation(loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), + hardware), + m_being_created(true), m_should_resolve_indirect_functions(false), + m_is_reexported(false), m_is_indirect(false), m_address(addr), + m_owner(owner), m_options_up(), m_bp_site_sp(), m_condition_mutex() { + if (check_for_resolver) { + Symbol *symbol = m_address.CalculateSymbolContextSymbol(); + if (symbol && symbol->IsIndirect()) { + SetShouldResolveIndirectFunctions(true); + } + } + + SetThreadID(tid); + m_being_created = false; +} + +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.get(); + 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) { + 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); + } + 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()); +} + +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 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 = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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->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.SetResultIsInternal( + 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() { + 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() { + if (m_options_up != nullptr) { + uint32_t loc_ignore = m_options_up->GetIgnoreCount(); + if (loc_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.reset(new BreakpointOptions(false)); + + return m_options_up.get(); +} + +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 = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); + + // Do this first, if a location is disabled, it shouldn't increment its hit + // count. + if (!IsEnabled()) + return false; + + if (!IgnoreCountShouldStop()) + return false; + + if (!m_owner.IgnoreCountShouldStop()) + 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. + IncrementHitCount(); + m_owner.IncrementHitCount(); + } +} + +void BreakpointLocation::UndoBumpHitCount() { + if (IsEnabled()) { + // Step our hit count, and also step the hit count of the owner. + DecrementHitCount(); + m_owner.DecrementHitCount(); + } +} + +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 = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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->RemoveOwnerFromBreakpointSite(GetBreakpoint().GetID(), + GetID(), m_bp_site_sp); + else + m_bp_site_sp->RemoveOwner(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); + } 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 (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()); + } + } + + if (level == lldb::eDescriptionLevelVerbose) { + s->EOL(); + s->Indent(); + s->Printf("resolved = %s\n", IsResolved() ? "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, hit count = %u ", (IsResolved() ? "" : "un"), + GetHitCount()); + if (m_options_up) { + m_options_up->GetDescription(s, level); + } + } +} + +void BreakpointLocation::Dump(Stream *s) const { + if (s == nullptr) + return; + + 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 " + "hw_index = %i 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", + IsHardware() ? "hardware" : "software", GetHardwareIndex(), + GetHitCount(), + GetOptionsSpecifyingKind(BreakpointOptions::eIgnoreCount) + ->GetIgnoreCount()); +} + +void BreakpointLocation::SendBreakpointLocationChangedEvent( + lldb::BreakpointEventType eventKind) { + if (!m_being_created && !m_owner.IsInternal() && + m_owner.GetTarget().EventTypeHasListeners( + Target::eBroadcastBitBreakpointChanged)) { + Breakpoint::BreakpointEventData *data = new Breakpoint::BreakpointEventData( + eventKind, m_owner.shared_from_this()); + data->GetBreakpointLocationCollection().Add(shared_from_this()); + m_owner.GetTarget().BroadcastEvent(Target::eBroadcastBitBreakpointChanged, + data); + } +} + +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(); +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointLocationCollection.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointLocationCollection.cpp new file mode 100644 index 00000000000..76084adbd2a --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointLocationCollection.cpp @@ -0,0 +1,187 @@ +//===-- BreakpointLocationCollection.cpp ------------------------*- C++ -*-===// +// +// 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() + : m_break_loc_collection(), m_collection_mutex() {} + +// Destructor +BreakpointLocationCollection::~BreakpointLocationCollection() {} + +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 + if (GetByIndex(i)->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/gnu/llvm/lldb/source/Breakpoint/BreakpointLocationList.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointLocationList.cpp new file mode 100644 index 00000000000..ee586127ee7 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointLocationList.cpp @@ -0,0 +1,312 @@ +//===-- BreakpointLocationList.cpp ------------------------------*- C++ -*-===// +// +// 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_locations(), m_address_to_location(), m_mutex(), + 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 = + std::lower_bound(m_locations.begin(), end, 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; +} + +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/gnu/llvm/lldb/source/Breakpoint/BreakpointName.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointName.cpp new file mode 100644 index 00000000000..749fa86bca9 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointName.cpp @@ -0,0 +1,86 @@ +//===-- BreakpointName.cpp --------------------------------------*- C++ -*-===// +// +// 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) +}; + +BreakpointName::BreakpointName(ConstString name, const Breakpoint &bkpt, + const char *help) : + m_name(name), m_options(bkpt.GetOptions()) +{ + SetHelp(help); +} + +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/gnu/llvm/lldb/source/Breakpoint/BreakpointOptions.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointOptions.cpp new file mode 100644 index 00000000000..8fd16f420c0 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointOptions.cpp @@ -0,0 +1,673 @@ +//===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// 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 found_something = false; + + bool success = options_dict.GetValueForKeyAsBoolean( + GetKey(OptionNames::StopOnError), data_up->stop_on_error); + + if (success) + found_something = true; + + 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; + } + + found_something = true; + 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) { + found_something = true; + size_t num_elems = user_source->GetSize(); + for (size_t i = 0; i < num_elems; i++) { + llvm::StringRef elem_string; + success = user_source->GetItemAtIndexAsString(i, elem_string); + if (success) + data_up->user_source.AppendString(elem_string); + } + } + + if (found_something) + return data_up; + else + return std::unique_ptr<BreakpointOptions::CommandData>(); +} + +const char *BreakpointOptions::g_option_names[( + size_t)BreakpointOptions::OptionNames::LastOptionName]{ + "ConditionText", "IgnoreCount", + "EnabledState", "OneShotState", "AutoContinue"}; + +bool BreakpointOptions::NullCallback(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) { + return true; +} + +// BreakpointOptions constructor +BreakpointOptions::BreakpointOptions(bool all_flags_set) + : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(), + m_baton_is_command_baton(false), m_callback_is_synchronous(false), + m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_up(), + m_condition_text(), m_condition_text_hash(0), 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_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_thread_spec_up(), + 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.reset(new 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.reset(new ThreadSpec(*rhs.m_thread_spec_up)); + m_condition_text = rhs.m_condition_text; + m_condition_text_hash = rhs.m_condition_text_hash; + 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.reset(new 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; + int32_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.SetErrorStringWithFormat( + "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.get(), 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 = BreakpointOptions::NullCallback; + 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()) { + // If a synchronous callback is called at async time, it should not say + // to stop. + return false; + } + } + return true; +} + +bool BreakpointOptions::HasCallback() const { + return m_callback != BreakpointOptions::NullCallback; +} + +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.reset(new 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) { + CommandReturnObject result; + Debugger &debugger = target->GetDebugger(); + // 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/gnu/llvm/lldb/source/Breakpoint/BreakpointPrecondition.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointPrecondition.cpp new file mode 100644 index 00000000000..a387c75c835 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointPrecondition.cpp @@ -0,0 +1,26 @@ +//===-- BreakpointPrecondition.cpp ------------------------------*- C++ -*-===// +// +// 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/gnu/llvm/lldb/source/Breakpoint/BreakpointResolver.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolver.cpp new file mode 100644 index 00000000000..e0a4e6ac671 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolver.cpp @@ -0,0 +1,352 @@ +//===-- BreakpointResolver.cpp ----------------------------------*- C++ -*-===// +// +// 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/Target.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/Stream.h" +#include "lldb/Utility/StreamString.h" + +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(Breakpoint *bkpt, + const unsigned char resolverTy, + lldb::addr_t offset) + : m_breakpoint(bkpt), m_offset(offset), SubclassID(resolverTy) {} + +BreakpointResolver::~BreakpointResolver() {} + +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.SetErrorStringWithFormat( + "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::addr_t offset; + success = subclass_options->GetValueForKeyAsInteger( + GetKey(OptionNames::Offset), offset); + if (!success) { + error.SetErrorString("Resolver data missing offset options key."); + return result_sp; + } + + BreakpointResolver *resolver; + + switch (resolver_type) { + case FileLineResolver: + resolver = BreakpointResolverFileLine::CreateFromStructuredData( + nullptr, *subclass_options, error); + break; + case AddressResolver: + resolver = BreakpointResolverAddress::CreateFromStructuredData( + nullptr, *subclass_options, error); + break; + case NameResolver: + resolver = BreakpointResolverName::CreateFromStructuredData( + nullptr, *subclass_options, error); + break; + case FileRegexResolver: + resolver = BreakpointResolverFileRegex::CreateFromStructuredData( + nullptr, *subclass_options, error); + break; + case PythonResolver: + resolver = BreakpointResolverScripted::CreateFromStructuredData( + nullptr, *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.Success()) { + return result_sp; + } else { + // Add on the global offset option: + resolver->SetOffset(offset); + return BreakpointResolverSP(resolver); + } +} + +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(Breakpoint *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; + uint32_t column; + SourceLoc(uint32_t l, uint32_t c) : line(l), column(c ? c : UINT32_MAX) {} + SourceLoc(const SymbolContext &sc) + : line(sc.line_entry.line), + column(sc.line_entry.column ? sc.line_entry.column : UINT32_MAX) {} +}; + +bool operator<(const SourceLoc a, const SourceLoc b) { + if (a.line < b.line) + return true; + if (a.line > b.line) + return false; + uint32_t a_col = a.column ? a.column : UINT32_MAX; + uint32_t b_col = b.column ? b.column : UINT32_MAX; + return a_col < b_col; +} +} // namespace + +void BreakpointResolver::SetSCMatchesByLine(SearchFilter &filter, + SymbolContextList &sc_list, + bool skip_prologue, + llvm::StringRef log_ident, + uint32_t line, uint32_t column) { + llvm::SmallVector<SymbolContext, 16> all_scs; + for (uint32_t i = 0; i < sc_list.GetSize(); ++i) + all_scs.push_back(sc_list[i]); + + 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.file == match.line_entry.file || + sc.line_entry.original_file == match.line_entry.original_file) { + // 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 after 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 SourceLoc(sc) < requested; }); + // 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(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_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 && !m_breakpoint->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 m_breakpoint->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/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverAddress.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverAddress.cpp new file mode 100644 index 00000000000..b98568098b4 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverAddress.cpp @@ -0,0 +1,184 @@ +//===-- BreakpointResolverAddress.cpp ---------------------------*- C++ -*-===// +// +// 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/Log.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +// BreakpointResolverAddress: +BreakpointResolverAddress::BreakpointResolverAddress( + Breakpoint *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(Breakpoint *bkpt, + const Address &addr) + : BreakpointResolver(bkpt, BreakpointResolver::AddressResolver), + m_addr(addr), m_resolved_addr(LLDB_INVALID_ADDRESS), m_module_filespec() { +} + +BreakpointResolverAddress::~BreakpointResolverAddress() {} + +BreakpointResolver *BreakpointResolverAddress::CreateFromStructuredData( + Breakpoint *bkpt, const StructuredData::Dictionary &options_dict, + Status &error) { + llvm::StringRef module_name; + lldb::addr_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 new BreakpointResolverAddress(bkpt, address, module_filespec); +} + +StructuredData::ObjectSP +BreakpointResolverAddress::SerializeToStructuredData() { + StructuredData::DictionarySP options_dict_sp( + new StructuredData::Dictionary()); + SectionSP section_sp = m_addr.GetSection(); + if (section_sp) { + ModuleSP module_sp = section_sp->GetModule(); + ConstString module_name; + if (module_sp) + module_name.SetCString(module_name.GetCString()); + + options_dict_sp->AddStringItem(GetKey(OptionNames::ModuleName), + module_name.GetCString()); + 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); + return StructuredData::ObjectSP(); +} + +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 (m_breakpoint->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 (m_breakpoint->GetNumLocations() == 0) + re_resolve = true; + + if (re_resolve) + BreakpointResolver::ResolveBreakpointInModules(filter, modules); +} + +Searcher::CallbackReturn BreakpointResolverAddress::SearchCallback( + SearchFilter &filter, SymbolContext &context, Address *addr) { + assert(m_breakpoint != nullptr); + + if (filter.AddressPasses(m_addr)) { + if (m_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 = m_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(&m_breakpoint->GetTarget()); + BreakpointLocationSP bp_loc_sp(AddLocation(m_addr)); + if (bp_loc_sp && !m_breakpoint->IsInternal()) { + StreamString s; + bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + Log *log( + lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + LLDB_LOGF(log, "Added location: %s\n", s.GetData()); + } + } else { + BreakpointLocationSP loc_sp = m_breakpoint->GetLocationAtIndex(0); + lldb::addr_t cur_load_location = + m_addr.GetLoadAddress(&m_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, m_breakpoint->GetTarget().GetProcessSP().get(), + Address::DumpStyleModuleWithFileAddress, + Address::DumpStyleLoadAddress); +} + +void BreakpointResolverAddress::Dump(Stream *s) const {} + +lldb::BreakpointResolverSP +BreakpointResolverAddress::CopyForBreakpoint(Breakpoint &breakpoint) { + lldb::BreakpointResolverSP ret_sp( + new BreakpointResolverAddress(&breakpoint, m_addr)); + return ret_sp; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp new file mode 100644 index 00000000000..2b26f65816b --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -0,0 +1,272 @@ +//===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===// +// +// 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/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +// BreakpointResolverFileLine: +BreakpointResolverFileLine::BreakpointResolverFileLine( + Breakpoint *bkpt, const FileSpec &file_spec, uint32_t line_no, + uint32_t column, lldb::addr_t offset, bool check_inlines, + bool skip_prologue, bool exact_match) + : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset), + m_file_spec(file_spec), m_line_number(line_no), m_column(column), + m_inlines(check_inlines), m_skip_prologue(skip_prologue), + m_exact_match(exact_match) {} + +BreakpointResolverFileLine::~BreakpointResolverFileLine() {} + +BreakpointResolver *BreakpointResolverFileLine::CreateFromStructuredData( + Breakpoint *bkpt, const StructuredData::Dictionary &options_dict, + Status &error) { + llvm::StringRef filename; + uint32_t line_no; + uint32_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_no); + 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; + } + + FileSpec file_spec(filename); + + return new BreakpointResolverFileLine(bkpt, file_spec, line_no, column, + offset, check_inlines, skip_prologue, + exact_match); +} + +StructuredData::ObjectSP +BreakpointResolverFileLine::SerializeToStructuredData() { + StructuredData::DictionarySP options_dict_sp( + new StructuredData::Dictionary()); + + options_dict_sp->AddStringItem(GetKey(OptionNames::FileName), + m_file_spec.GetPath()); + options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber), + m_line_number); + options_dict_sp->AddIntegerItem(GetKey(OptionNames::Column), + m_column); + options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines), m_inlines); + options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue), + m_skip_prologue); + options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch), + m_exact_match); + + 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, + bool is_relative) { + if (m_exact_match) + return; // Nothing to do. Contexts are precise. + + llvm::StringRef relative_path; + if (is_relative) + relative_path = m_file_spec.GetDirectory().GetStringRef(); + + Log * log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); + for(uint32_t i = 0; i < sc_list.GetSize(); ++i) { + SymbolContext sc; + sc_list.GetContextAtIndex(i, sc); + if (is_relative) { + // If the path was relative, make sure any matches match as long as the + // relative parts of the path match the path from support files + auto sc_dir = sc.line_entry.file.GetDirectory().GetStringRef(); + if (!sc_dir.endswith(relative_path)) { + // We had a relative path specified and the relative directory doesn't + // match so remove this one + LLDB_LOG(log, "removing not matching relative path {0} since it " + "doesn't end with {1}", sc_dir, relative_path); + sc_list.RemoveContextAtIndex(i); + --i; + continue; + } + } + + 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.file) { + LLDB_LOG(log, "unexpected symbol context file {0}", sc.line_entry.file); + 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. + const int decl_line_is_too_late_fudge = 1; + if (line && m_line_number < line - decl_line_is_too_late_fudge) { + LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line); + sc_list.RemoveContextAtIndex(i); + --i; + } + } +} + +Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback( + SearchFilter &filter, SymbolContext &context, Address *addr) { + SymbolContextList sc_list; + + assert(m_breakpoint != nullptr); + + // 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. + + FileSpec search_file_spec = m_file_spec; + const bool is_relative = m_file_spec.IsRelative(); + if (is_relative) + search_file_spec.GetDirectory().Clear(); + + 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(search_file_spec, m_line_number, m_inlines, + m_exact_match, eSymbolContextEverything, + sc_list); + } + } + + FilterContexts(sc_list, is_relative); + + StreamString s; + s.Printf("for %s:%d ", m_file_spec.GetFilename().AsCString("<Unknown>"), + m_line_number); + + SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), + m_line_number, m_column); + + return Searcher::eCallbackReturnContinue; +} + +lldb::SearchDepth BreakpointResolverFileLine::GetDepth() { + return lldb::eSearchDepthModule; +} + +void BreakpointResolverFileLine::GetDescription(Stream *s) { + s->Printf("file = '%s', line = %u, ", m_file_spec.GetPath().c_str(), + m_line_number); + if (m_column) + s->Printf("column = %u, ", m_column); + s->Printf("exact_match = %d", m_exact_match); +} + +void BreakpointResolverFileLine::Dump(Stream *s) const {} + +lldb::BreakpointResolverSP +BreakpointResolverFileLine::CopyForBreakpoint(Breakpoint &breakpoint) { + lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine( + &breakpoint, m_file_spec, m_line_number, m_column, m_offset, m_inlines, + m_skip_prologue, m_exact_match)); + + return ret_sp; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp new file mode 100644 index 00000000000..6b600a7cf12 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverFileRegex.cpp @@ -0,0 +1,172 @@ +//===-- BreakpointResolverFileRegex.cpp -------------------------*- C++-*-===// +// +// 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( + Breakpoint *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) {} + +BreakpointResolverFileRegex::~BreakpointResolverFileRegex() {} + +BreakpointResolver *BreakpointResolverFileRegex::CreateFromStructuredData( + Breakpoint *bkpt, 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++) { + llvm::StringRef name; + success = names_array->GetItemAtIndexAsString(i, name); + if (!success) { + error.SetErrorStringWithFormat( + "BRFR::CFSD: Malformed element %zu in the names array.", i); + return nullptr; + } + names_set.insert(name); + } + } + + return new BreakpointResolverFileRegex(bkpt, 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) { + + assert(m_breakpoint != nullptr); + 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; + const bool search_inlines = false; + + cu->ResolveSymbolContext(cu_file_spec, line_matches[i], search_inlines, + m_exact_match, 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()); + } + assert(m_breakpoint != nullptr); + + 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(Breakpoint &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/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverName.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverName.cpp new file mode 100644 index 00000000000..ba9c88c7eae --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverName.cpp @@ -0,0 +1,433 @@ +//===-- BreakpointResolverName.cpp ------------------------------*- C++ -*-===// +// +// 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/Target.h" +#include "lldb/Target/Language.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointResolverName::BreakpointResolverName( + Breakpoint *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_class_name(), m_regex(), m_match_type(type), m_language(language), + m_skip_prologue(skip_prologue) { + if (m_match_type == Breakpoint::Regexp) { + m_regex = RegularExpression(llvm::StringRef::withNullAsEmpty(name_cstr)); + if (!m_regex.IsValid()) { + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + + if (log) + log->Warning("function name regexp: \"%s\" did not compile.", + name_cstr); + } + } else { + AddNameLookup(ConstString(name_cstr), name_type_mask); + } +} + +BreakpointResolverName::BreakpointResolverName( + Breakpoint *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(Breakpoint *bkpt, + 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(Breakpoint *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() = default; + +BreakpointResolverName::BreakpointResolverName( + const BreakpointResolverName &rhs) + : BreakpointResolver(rhs.m_breakpoint, BreakpointResolver::NameResolver, + rhs.m_offset), + 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) {} + +BreakpointResolver *BreakpointResolverName::CreateFromStructuredData( + Breakpoint *bkpt, 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::addr_t offset = 0; + success = + options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Offset), offset); + if (!success) { + error.SetErrorStringWithFormat("BRN::CFSD: Missing offset entry."); + return nullptr; + } + + bool skip_prologue; + success = options_dict.GetValueForKeyAsBoolean( + GetKey(OptionNames::SkipPrologue), skip_prologue); + if (!success) { + error.SetErrorStringWithFormat("BRN::CFSD: Missing Skip prologue entry."); + return nullptr; + } + + llvm::StringRef regex_text; + success = options_dict.GetValueForKeyAsString( + GetKey(OptionNames::RegexString), regex_text); + if (success) { + return new BreakpointResolverName(bkpt, RegularExpression(regex_text), + language, offset, skip_prologue); + } else { + StructuredData::Array *names_array; + success = options_dict.GetValueForKeyAsArray( + GetKey(OptionNames::SymbolNameArray), names_array); + if (!success) { + error.SetErrorStringWithFormat("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.SetErrorStringWithFormat( + "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++) { + llvm::StringRef name; + + success = names_array->GetItemAtIndexAsString(i, name); + if (!success) { + error.SetErrorString("BRN::CFSD: name entry is not a string."); + return nullptr; + } + std::underlying_type<FunctionNameType>::type fnt; + success = names_mask_array->GetItemAtIndexAsInteger(i, fnt); + if (!success) { + error.SetErrorString("BRN::CFSD: name mask entry is not an integer."); + return nullptr; + } + names.push_back(name); + name_masks.push_back(static_cast<FunctionNameType>(fnt)); + } + + BreakpointResolverName *resolver = new BreakpointResolverName( + bkpt, names[0].c_str(), name_masks[0], language, + Breakpoint::MatchType::Exact, offset, skip_prologue); + for (size_t i = 1; i < num_elem; i++) { + resolver->AddNameLookup(ConstString(names[i]), name_masks[i]); + } + return resolver; + } +} + +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().AsCString()))); + name_masks_sp->AddItem(StructuredData::IntegerSP( + new StructuredData::Integer(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 (ConstString variant_name : lang->GetMethodNameVariants(name)) { + Module::LookupInfo variant_lookup(name, name_type_mask, + lang->GetLanguageType()); + variant_lookup.SetLookupName(variant_name); + 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) { + SymbolContextList func_list; + // SymbolContextList sym_list; + + uint32_t i; + bool new_location; + Address break_addr; + assert(m_breakpoint != nullptr); + + Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS)); + + if (m_class_name) { + if (log) + log->Warning("Class/method function specification not supported yet.\n"); + return Searcher::eCallbackReturnStop; + } + bool filter_by_cu = + (filter.GetFilterRequiredItems() & eSymbolContextCompUnit) != 0; + bool filter_by_language = (m_language != eLanguageTypeUnknown); + const bool include_symbols = !filter_by_cu; + const bool 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.GetLookupName(), nullptr, lookup.GetNameTypeMask(), + include_symbols, include_inlines, 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, + !filter_by_cu, // include symbols only if we aren't filtering by CU + include_inlines, 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--; + } + } + } + + // Remove any duplicates between the function list and the symbol list + SymbolContext sc; + if (func_list.GetSize()) { + for (i = 0; i < func_list.GetSize(); i++) { + if (func_list.GetContextAtIndex(i, sc)) { + 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(m_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 = + m_breakpoint->GetTarget().GetArchitecturePlugin(); + if (arch) + arch->AdjustBreakpointAddress(*sc.symbol, break_addr); + } + } + } + + if (break_addr.IsValid()) { + if (filter.AddressPasses(break_addr)) { + BreakpointLocationSP bp_loc_sp( + AddLocation(break_addr, &new_location)); + bp_loc_sp->SetIsReExported(is_reexported); + if (bp_loc_sp && new_location && !m_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(Breakpoint &breakpoint) { + lldb::BreakpointResolverSP ret_sp(new BreakpointResolverName(*this)); + ret_sp->SetBreakpoint(&breakpoint); + return ret_sp; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverScripted.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverScripted.cpp new file mode 100644 index 00000000000..288fd37c1c7 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointResolverScripted.cpp @@ -0,0 +1,163 @@ +//===-- BreakpointResolverScripted.cpp ---------------------------*- C++ -*-===// +// +// 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( + Breakpoint *bkpt, + const llvm::StringRef class_name, + lldb::SearchDepth depth, + StructuredDataImpl *args_data) + : BreakpointResolver(bkpt, BreakpointResolver::PythonResolver), + m_class_name(class_name), m_depth(depth), m_args_ptr(args_data) { + CreateImplementationIfNeeded(); +} + +void BreakpointResolverScripted::CreateImplementationIfNeeded() { + if (m_implementation_sp) + return; + + if (m_class_name.empty()) + return; + + if (m_breakpoint) { + TargetSP target_sp = m_breakpoint->GetTargetSP(); + ScriptInterpreter *script_interp = target_sp->GetDebugger() + .GetScriptInterpreter(); + if (!script_interp) + return; + lldb::BreakpointSP bkpt_sp(m_breakpoint->shared_from_this()); + m_implementation_sp = script_interp->CreateScriptedBreakpointResolver( + m_class_name.c_str(), m_args_ptr, bkpt_sp); + } +} + +void BreakpointResolverScripted::NotifyBreakpointSet() { + CreateImplementationIfNeeded(); +} + +BreakpointResolverScripted::~BreakpointResolverScripted() {} + +BreakpointResolver * +BreakpointResolverScripted::CreateFromStructuredData( + Breakpoint *bkpt, 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 = new StructuredDataImpl(); + StructuredData::Dictionary *args_dict = nullptr; + success = options_dict.GetValueForKeyAsDictionary( + GetKey(OptionNames::ScriptArgs), args_dict); + if (success) { + args_data_impl->SetObjectSP(args_dict->shared_from_this()); + } + return new BreakpointResolverScripted(bkpt, 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_ptr->IsValid()) + options_dict_sp->AddItem(GetKey(OptionNames::ScriptArgs), + m_args_ptr->GetObjectSP()); + + return WrapOptionsDict(options_dict_sp); +} + +ScriptInterpreter *BreakpointResolverScripted::GetScriptInterpreter() { + return m_breakpoint->GetTarget().GetDebugger().GetScriptInterpreter(); +} + +Searcher::CallbackReturn BreakpointResolverScripted::SearchCallback( + SearchFilter &filter, SymbolContext &context, Address *addr) { + assert(m_breakpoint != nullptr); + 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; + else + return Searcher::eCallbackReturnStop; +} + +lldb::SearchDepth +BreakpointResolverScripted::GetDepth() { + assert(m_breakpoint != nullptr); + 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(Breakpoint &breakpoint) { + // FIXME: Have to make a copy of the arguments from the m_args_ptr and then + // pass that to the new resolver. + lldb::BreakpointResolverSP ret_sp( + new BreakpointResolverScripted(&breakpoint, m_class_name, m_depth, + nullptr)); + return ret_sp; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointSite.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointSite.cpp new file mode 100644 index 00000000000..a757a01824c --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointSite.cpp @@ -0,0 +1,206 @@ +//===-- BreakpointSite.cpp --------------------------------------*- C++ -*-===// +// +// 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 <inttypes.h> + +#include "lldb/Breakpoint/BreakpointSite.h" + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSiteList.h" +#include "lldb/Utility/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointSite::BreakpointSite(BreakpointSiteList *list, + const BreakpointLocationSP &owner, + lldb::addr_t addr, bool use_hardware) + : StoppointLocation(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_owners(), m_owners_mutex() { + m_owners.Add(owner); +} + +BreakpointSite::~BreakpointSite() { + BreakpointLocationSP bp_loc_sp; + const size_t owner_count = m_owners.GetSize(); + for (size_t i = 0; i < owner_count; i++) { + m_owners.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) { + IncrementHitCount(); + // ShouldStop can do a lot of work, and might even come come back and hit + // this breakpoint site again. So don't hold the m_owners_mutex the whole + // while. Instead make a local copy of the collection and call ShouldStop on + // the copy. + BreakpointLocationCollection owners_copy; + { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + owners_copy = m_owners; + } + return owners_copy.ShouldStop(context); +} + +bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + const size_t owner_count = m_owners.GetSize(); + for (size_t i = 0; i < owner_count; i++) { + if (m_owners.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 hw_index = %i hit_count = %-4u", + GetID(), (uint64_t)m_addr, IsHardware() ? "hardware" : "software", + GetHardwareIndex(), GetHitCount()); +} + +void BreakpointSite::GetDescription(Stream *s, lldb::DescriptionLevel level) { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + if (level != lldb::eDescriptionLevelBrief) + s->Printf("breakpoint site: %d at 0x%8.8" PRIx64, GetID(), + GetLoadAddress()); + m_owners.GetDescription(s, level); +} + +bool BreakpointSite::IsInternal() const { return m_owners.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::AddOwner(const BreakpointLocationSP &owner) { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + m_owners.Add(owner); +} + +size_t BreakpointSite::RemoveOwner(lldb::break_id_t break_id, + lldb::break_id_t break_loc_id) { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + m_owners.Remove(break_id, break_loc_id); + return m_owners.GetSize(); +} + +size_t BreakpointSite::GetNumberOfOwners() { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + return m_owners.GetSize(); +} + +BreakpointLocationSP BreakpointSite::GetOwnerAtIndex(size_t index) { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + return m_owners.GetByIndex(index); +} + +bool BreakpointSite::ValidForThisThread(Thread *thread) { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + return m_owners.ValidForThisThread(thread); +} + +void BreakpointSite::BumpHitCounts() { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + for (BreakpointLocationSP loc_sp : m_owners.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 { + // We only use software traps for software breakpoints + if (!IsHardware()) { + if (m_byte_size > 0) { + 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; + } + } + return false; +} + +size_t +BreakpointSite::CopyOwnersList(BreakpointLocationCollection &out_collection) { + std::lock_guard<std::recursive_mutex> guard(m_owners_mutex); + for (BreakpointLocationSP loc_sp : m_owners.BreakpointLocations()) { + out_collection.Add(loc_sp); + } + return out_collection.GetSize(); +} diff --git a/gnu/llvm/lldb/source/Breakpoint/BreakpointSiteList.cpp b/gnu/llvm/lldb/source/Breakpoint/BreakpointSiteList.cpp new file mode 100644 index 00000000000..7a986fd8398 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/BreakpointSiteList.cpp @@ -0,0 +1,200 @@ +//===-- BreakpointSiteList.cpp ----------------------------------*- C++ -*-===// +// +// 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/BreakpointSiteList.h" + +#include "lldb/Utility/Stream.h" +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +BreakpointSiteList::BreakpointSiteList() : m_mutex(), m_bp_site_list() {} + +BreakpointSiteList::~BreakpointSiteList() {} + +// Add breakpoint site to the list. However, if the element already exists in +// the list, then we don't add it, and return LLDB_INVALID_BREAK_ID. + +lldb::break_id_t BreakpointSiteList::Add(const BreakpointSiteSP &bp) { + lldb::addr_t bp_site_load_addr = bp->GetLoadAddress(); + std::lock_guard<std::recursive_mutex> guard(m_mutex); + collection::iterator iter = m_bp_site_list.find(bp_site_load_addr); + + if (iter == m_bp_site_list.end()) { + m_bp_site_list.insert(iter, collection::value_type(bp_site_load_addr, bp)); + return bp->GetID(); + } else { + return LLDB_INVALID_BREAK_ID; + } +} + +bool BreakpointSiteList::ShouldStop(StoppointCallbackContext *context, + lldb::break_id_t site_id) { + BreakpointSiteSP site_sp(FindByID(site_id)); + if (site_sp) { + // Let the BreakpointSite 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 site_sp->ShouldStop(context); + } + // We should stop here since this BreakpointSite isn't valid anymore or it + // doesn't exist. + return true; +} +lldb::break_id_t BreakpointSiteList::FindIDByAddress(lldb::addr_t addr) { + BreakpointSiteSP bp = FindByAddress(addr); + if (bp) { + // DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8" + // PRIx64 " ) => %u", __FUNCTION__, (uint64_t)addr, bp->GetID()); + return bp.get()->GetID(); + } + // DBLogIf(PD_LOG_BREAKPOINTS, "BreakpointSiteList::%s ( addr = 0x%8.8" + // PRIx64 + // " ) => NONE", __FUNCTION__, (uint64_t)addr); + return LLDB_INVALID_BREAK_ID; +} + +bool BreakpointSiteList::Remove(lldb::break_id_t break_id) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + collection::iterator pos = GetIDIterator(break_id); // Predicate + if (pos != m_bp_site_list.end()) { + m_bp_site_list.erase(pos); + return true; + } + return false; +} + +bool BreakpointSiteList::RemoveByAddress(lldb::addr_t address) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + collection::iterator pos = m_bp_site_list.find(address); + if (pos != m_bp_site_list.end()) { + m_bp_site_list.erase(pos); + return true; + } + return false; +} + +class BreakpointSiteIDMatches { +public: + BreakpointSiteIDMatches(lldb::break_id_t break_id) : m_break_id(break_id) {} + + bool operator()(std::pair<lldb::addr_t, BreakpointSiteSP> val_pair) const { + return m_break_id == val_pair.second->GetID(); + } + +private: + const lldb::break_id_t m_break_id; +}; + +BreakpointSiteList::collection::iterator +BreakpointSiteList::GetIDIterator(lldb::break_id_t break_id) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + return std::find_if(m_bp_site_list.begin(), + m_bp_site_list.end(), // Search full range + BreakpointSiteIDMatches(break_id)); // Predicate +} + +BreakpointSiteList::collection::const_iterator +BreakpointSiteList::GetIDConstIterator(lldb::break_id_t break_id) const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + return std::find_if(m_bp_site_list.begin(), + m_bp_site_list.end(), // Search full range + BreakpointSiteIDMatches(break_id)); // Predicate +} + +BreakpointSiteSP BreakpointSiteList::FindByID(lldb::break_id_t break_id) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + BreakpointSiteSP stop_sp; + collection::iterator pos = GetIDIterator(break_id); + if (pos != m_bp_site_list.end()) + stop_sp = pos->second; + + return stop_sp; +} + +const BreakpointSiteSP +BreakpointSiteList::FindByID(lldb::break_id_t break_id) const { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + BreakpointSiteSP stop_sp; + collection::const_iterator pos = GetIDConstIterator(break_id); + if (pos != m_bp_site_list.end()) + stop_sp = pos->second; + + return stop_sp; +} + +BreakpointSiteSP BreakpointSiteList::FindByAddress(lldb::addr_t addr) { + BreakpointSiteSP found_sp; + std::lock_guard<std::recursive_mutex> guard(m_mutex); + collection::iterator iter = m_bp_site_list.find(addr); + if (iter != m_bp_site_list.end()) + found_sp = iter->second; + return found_sp; +} + +bool BreakpointSiteList::BreakpointSiteContainsBreakpoint( + lldb::break_id_t bp_site_id, lldb::break_id_t bp_id) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + collection::const_iterator pos = GetIDConstIterator(bp_site_id); + if (pos != m_bp_site_list.end()) + return pos->second->IsBreakpointAtThisSite(bp_id); + + return false; +} + +void BreakpointSiteList::Dump(Stream *s) const { + s->Printf("%p: ", static_cast<const void *>(this)); + // s->Indent(); + s->Printf("BreakpointSiteList with %u BreakpointSites:\n", + (uint32_t)m_bp_site_list.size()); + s->IndentMore(); + collection::const_iterator pos; + collection::const_iterator end = m_bp_site_list.end(); + for (pos = m_bp_site_list.begin(); pos != end; ++pos) + pos->second->Dump(s); + s->IndentLess(); +} + +void BreakpointSiteList::ForEach( + std::function<void(BreakpointSite *)> const &callback) { + std::lock_guard<std::recursive_mutex> guard(m_mutex); + for (auto pair : m_bp_site_list) + callback(pair.second.get()); +} + +bool BreakpointSiteList::FindInRange(lldb::addr_t lower_bound, + lldb::addr_t upper_bound, + BreakpointSiteList &bp_site_list) const { + if (lower_bound > upper_bound) + return false; + + std::lock_guard<std::recursive_mutex> guard(m_mutex); + collection::const_iterator lower, upper, pos; + lower = m_bp_site_list.lower_bound(lower_bound); + if (lower == m_bp_site_list.end() || (*lower).first >= upper_bound) + return false; + + // This is one tricky bit. The breakpoint might overlap the bottom end of + // the range. So we grab the breakpoint prior to the lower bound, and check + // that that + its byte size isn't in our range. + if (lower != m_bp_site_list.begin()) { + collection::const_iterator prev_pos = lower; + prev_pos--; + const BreakpointSiteSP &prev_bp = (*prev_pos).second; + if (prev_bp->GetLoadAddress() + prev_bp->GetByteSize() > lower_bound) + bp_site_list.Add(prev_bp); + } + + upper = m_bp_site_list.upper_bound(upper_bound); + + for (pos = lower; pos != upper; pos++) { + bp_site_list.Add((*pos).second); + } + return true; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/CMakeLists.txt b/gnu/llvm/lldb/source/Breakpoint/CMakeLists.txt new file mode 100644 index 00000000000..a7c0baf21af --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/CMakeLists.txt @@ -0,0 +1,37 @@ +add_lldb_library(lldbBreakpoint + Breakpoint.cpp + BreakpointID.cpp + BreakpointIDList.cpp + BreakpointList.cpp + BreakpointLocation.cpp + BreakpointLocationCollection.cpp + BreakpointLocationList.cpp + BreakpointName.cpp + BreakpointOptions.cpp + BreakpointPrecondition.cpp + BreakpointResolver.cpp + BreakpointResolverAddress.cpp + BreakpointResolverFileLine.cpp + BreakpointResolverFileRegex.cpp + BreakpointResolverName.cpp + BreakpointResolverScripted.cpp + BreakpointSite.cpp + BreakpointSiteList.cpp + Stoppoint.cpp + StoppointCallbackContext.cpp + StoppointLocation.cpp + Watchpoint.cpp + WatchpointList.cpp + WatchpointOptions.cpp + + LINK_LIBS + lldbCore + lldbExpression + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + + LINK_COMPONENTS + Support + ) diff --git a/gnu/llvm/lldb/source/Breakpoint/Stoppoint.cpp b/gnu/llvm/lldb/source/Breakpoint/Stoppoint.cpp new file mode 100644 index 00000000000..4cab975fe32 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/Stoppoint.cpp @@ -0,0 +1,24 @@ +//===-- Stoppoint.cpp -------------------------------------------*- C++ -*-===// +// +// 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() : m_bid(LLDB_INVALID_BREAK_ID) {} + +// Destructor +Stoppoint::~Stoppoint() {} + +break_id_t Stoppoint::GetID() const { return m_bid; } + +void Stoppoint::SetID(break_id_t bid) { m_bid = bid; } diff --git a/gnu/llvm/lldb/source/Breakpoint/StoppointCallbackContext.cpp b/gnu/llvm/lldb/source/Breakpoint/StoppointCallbackContext.cpp new file mode 100644 index 00000000000..584bf0060a4 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/StoppointCallbackContext.cpp @@ -0,0 +1,24 @@ +//===-- StoppointCallbackContext.cpp ----------------------------*- C++ -*-===// +// +// 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() + : event(nullptr), exe_ctx_ref(), is_synchronous(false) {} + +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/gnu/llvm/lldb/source/Breakpoint/StoppointLocation.cpp b/gnu/llvm/lldb/source/Breakpoint/StoppointLocation.cpp new file mode 100644 index 00000000000..8cc6791fa68 --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/StoppointLocation.cpp @@ -0,0 +1,32 @@ +//===-- StoppointLocation.cpp -----------------------------------*- C++ -*-===// +// +// 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/StoppointLocation.h" + + +using namespace lldb; +using namespace lldb_private; + +// StoppointLocation constructor +StoppointLocation::StoppointLocation(break_id_t bid, addr_t addr, bool hardware) + : m_loc_id(bid), m_addr(addr), m_hardware(hardware), + m_hardware_index(LLDB_INVALID_INDEX32), m_byte_size(0), m_hit_count(0) {} + +StoppointLocation::StoppointLocation(break_id_t bid, addr_t addr, + uint32_t byte_size, bool hardware) + : m_loc_id(bid), m_addr(addr), m_hardware(hardware), + m_hardware_index(LLDB_INVALID_INDEX32), m_byte_size(byte_size), + m_hit_count(0) {} + +// Destructor +StoppointLocation::~StoppointLocation() {} + +void StoppointLocation::DecrementHitCount() { + assert(m_hit_count > 0); + --m_hit_count; +} diff --git a/gnu/llvm/lldb/source/Breakpoint/Watchpoint.cpp b/gnu/llvm/lldb/source/Breakpoint/Watchpoint.cpp new file mode 100644 index 00000000000..17dcda13e9b --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/Watchpoint.cpp @@ -0,0 +1,388 @@ +//===-- Watchpoint.cpp ------------------------------------------*- C++ -*-===// +// +// 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/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectMemory.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/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) + : StoppointLocation(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_was_read(0), m_watch_was_written(0), + m_ignore_count(0), m_false_alarms(0), m_decl_str(), m_watch_spec_str(), + m_type(), m_error(), m_options(), m_being_created(true) { + + 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( + lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS), + std::move(err), "Failed to set type."); + } else { + m_type = type_system_or_err->GetBuiltinTypeForEncodingAndBitSize( + eEncodingUint, 8 * size); + } + } + + // Set the initial value of the watched variable: + if (m_target.GetProcessSP()) { + ExecutionContext exe_ctx; + m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx); + CaptureWatchedValue(exe_ctx); + } + m_being_created = false; +} + +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 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); +} + +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; +} + +// Override default impl of StoppointLocation::IsHardware() since m_is_hardware +// member field is more accurate. +bool Watchpoint::IsHardware() const { 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 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(), watch_name.GetStringRef(), + watch_address, m_type); + m_new_value_sp = m_new_value_sp->CreateConstantValue(watch_name); + return (m_new_value_sp && m_new_value_sp->GetError().Success()); +} + +void Watchpoint::IncrementFalseAlarmsAndReviseHitCount() { + ++m_false_alarms; + if (m_false_alarms) { + if (m_hit_count >= m_false_alarms) { + m_hit_count -= m_false_alarms; + m_false_alarms = 0; + } else { + m_false_alarms -= m_hit_count; + m_hit_count = 0; + } + } +} + +// RETURNS - true if we should stop at this breakpoint, false if we +// should continue. + +bool Watchpoint::ShouldStop(StoppointCallbackContext *context) { + IncrementHitCount(); + + 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. +void Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const { + if (!prefix) { + s->Printf("\nWatchpoint %u hit:", GetID()); + prefix = ""; + } + + if (m_old_value_sp) { + const char *old_value_cstr = m_old_value_sp->GetValueAsCString(); + if (old_value_cstr && old_value_cstr[0]) + s->Printf("\n%sold value: %s", prefix, old_value_cstr); + else { + const char *old_summary_cstr = m_old_value_sp->GetSummaryAsCString(); + if (old_summary_cstr && old_summary_cstr[0]) + s->Printf("\n%sold value: %s", prefix, old_summary_cstr); + } + } + + if (m_new_value_sp) { + const char *new_value_cstr = m_new_value_sp->GetValueAsCString(); + if (new_value_cstr && new_value_cstr[0]) + s->Printf("\n%snew value: %s", prefix, new_value_cstr); + else { + const char *new_summary_cstr = m_new_value_sp->GetSummaryAsCString(); + if (new_summary_cstr && new_summary_cstr[0]) + s->Printf("\n%snew value: %s", prefix, new_summary_cstr); + } + } +} + +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", + GetID(), GetLoadAddress(), m_byte_size, + IsEnabled() ? "enabled" : "disabled", m_watch_read ? "r" : "", + m_watch_write ? "w" : ""); + + 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()); + + // 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 hw_index = %i hit_count = %-4u ignore_count = %-4u", + GetHardwareIndex(), 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) + SetHardwareIndex(LLDB_INVALID_INDEX32); + else + ++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; + m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0; + m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0; + if (notify && + (old_watch_read != m_watch_read || old_watch_write != m_watch_write)) + SendWatchpointChangedEvent(eWatchpointEventTypeTypeChanged); +} + +bool Watchpoint::WatchpointRead() const { return m_watch_read != 0; } + +bool Watchpoint::WatchpointWrite() const { return m_watch_write != 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, llvm::StringRef(), lldb::eLanguageTypeUnknown, + 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 (!m_being_created && + GetTarget().EventTypeHasListeners( + Target::eBroadcastBitWatchpointChanged)) { + WatchpointEventData *data = + new Watchpoint::WatchpointEventData(eventKind, shared_from_this()); + GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, data); + } +} + +void Watchpoint::SendWatchpointChangedEvent(WatchpointEventData *data) { + if (data == nullptr) + return; + + if (!m_being_created && + GetTarget().EventTypeHasListeners(Target::eBroadcastBitWatchpointChanged)) + GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, data); + else + delete data; +} + +Watchpoint::WatchpointEventData::WatchpointEventData( + WatchpointEventType sub_type, const WatchpointSP &new_watchpoint_sp) + : EventData(), m_watchpoint_event(sub_type), + m_new_watchpoint_sp(new_watchpoint_sp) {} + +Watchpoint::WatchpointEventData::~WatchpointEventData() = default; + +ConstString Watchpoint::WatchpointEventData::GetFlavorString() { + static ConstString g_flavor("Watchpoint::WatchpointEventData"); + return g_flavor; +} + +ConstString 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/gnu/llvm/lldb/source/Breakpoint/WatchpointList.cpp b/gnu/llvm/lldb/source/Breakpoint/WatchpointList.cpp new file mode 100644 index 00000000000..b1c1e6f253e --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/WatchpointList.cpp @@ -0,0 +1,252 @@ +//===-- WatchpointList.cpp --------------------------------------*- C++ -*-===// +// +// 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() + : m_watchpoints(), m_mutex(), m_next_wp_id(0) {} + +WatchpointList::~WatchpointList() {} + +// 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)) + wp_sp->GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, + new Watchpoint::WatchpointEventData( + eWatchpointEventTypeAdded, wp_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)) + wp_sp->GetTarget().BroadcastEvent( + Target::eBroadcastBitWatchpointChanged, + new Watchpoint::WatchpointEventData(eWatchpointEventTypeRemoved, + wp_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)) { + (*pos)->GetTarget().BroadcastEvent( + Target::eBroadcastBitWatchpointChanged, + new Watchpoint::WatchpointEventData(eWatchpointEventTypeRemoved, + *pos)); + } + } + } + } + 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/gnu/llvm/lldb/source/Breakpoint/WatchpointOptions.cpp b/gnu/llvm/lldb/source/Breakpoint/WatchpointOptions.cpp new file mode 100644 index 00000000000..026bf2f746a --- /dev/null +++ b/gnu/llvm/lldb/source/Breakpoint/WatchpointOptions.cpp @@ -0,0 +1,182 @@ +//===-- WatchpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// 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), m_callback_baton_sp(), + m_callback_is_synchronous(false), m_thread_spec_up() {} + +// 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), + m_thread_spec_up() { + if (rhs.m_thread_spec_up != nullptr) + m_thread_spec_up.reset(new 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.reset(new 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.reset(new 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"; +} |