diff options
| author | Ed Maste <emaste@FreeBSD.org> | 2013-08-23 17:46:38 +0000 |
|---|---|---|
| committer | Ed Maste <emaste@FreeBSD.org> | 2013-08-23 17:46:38 +0000 |
| commit | f034231a6a1fd5d6395206c1651de8cd9402cca3 (patch) | |
| tree | f561dabc721ad515599172c16da3a4400b7f4aec /source/Breakpoint | |
Notes
Diffstat (limited to 'source/Breakpoint')
21 files changed, 5587 insertions, 0 deletions
diff --git a/source/Breakpoint/Breakpoint.cpp b/source/Breakpoint/Breakpoint.cpp new file mode 100644 index 000000000000..9bc43814b48a --- /dev/null +++ b/source/Breakpoint/Breakpoint.cpp @@ -0,0 +1,794 @@ +//===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/lldb-private-log.h" +#include "llvm/Support/Casting.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +const ConstString & +Breakpoint::GetEventIdentifier () +{ + static ConstString g_identifier("event-identifier.breakpoint.changed"); + return g_identifier; +} + +//---------------------------------------------------------------------- +// Breakpoint constructor +//---------------------------------------------------------------------- +Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp) : + m_being_created(true), + m_target (target), + m_filter_sp (filter_sp), + m_resolver_sp (resolver_sp), + m_options (), + m_locations (*this) +{ + m_being_created = false; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Breakpoint::~Breakpoint() +{ +} + +bool +Breakpoint::IsInternal () const +{ + return LLDB_BREAK_ID_IS_INTERNAL(m_bid); +} + + + +Target& +Breakpoint::GetTarget () +{ + return m_target; +} + +const Target& +Breakpoint::GetTarget () const +{ + return m_target; +} + +BreakpointLocationSP +Breakpoint::AddLocation (const Address &addr, bool *new_location) +{ + return m_locations.AddLocation (addr, 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); +} + +// For each of the overall options we need to decide how they propagate to +// the location options. This will determine the precedence of options on +// the breakpoint vs. its locations. + +// Disable at the breakpoint level should override the location settings. +// That way you can conveniently turn off a whole breakpoint without messing +// up the individual settings. + +void +Breakpoint::SetEnabled (bool enable) +{ + if (enable == m_options.IsEnabled()) + return; + + m_options.SetEnabled(enable); + if (enable) + m_locations.ResolveAllBreakpointSites(); + else + m_locations.ClearAllBreakpointSites(); + + SendBreakpointChangedEvent (enable ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled); + +} + +bool +Breakpoint::IsEnabled () +{ + return m_options.IsEnabled(); +} + +void +Breakpoint::SetIgnoreCount (uint32_t n) +{ + if (m_options.GetIgnoreCount() == n) + return; + + m_options.SetIgnoreCount(n); + SendBreakpointChangedEvent (eBreakpointEventTypeIgnoreChanged); +} + +void +Breakpoint::DecrementIgnoreCount () +{ + uint32_t ignore = m_options.GetIgnoreCount(); + if (ignore != 0) + m_options.SetIgnoreCount(ignore - 1); +} + +uint32_t +Breakpoint::GetIgnoreCount () const +{ + return m_options.GetIgnoreCount(); +} + +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_locations.GetHitCount(); +} + +bool +Breakpoint::IsOneShot () const +{ + return m_options.IsOneShot(); +} + +void +Breakpoint::SetOneShot (bool one_shot) +{ + m_options.SetOneShot (one_shot); +} + +void +Breakpoint::SetThreadID (lldb::tid_t thread_id) +{ + if (m_options.GetThreadSpec()->GetTID() == thread_id) + return; + + m_options.GetThreadSpec()->SetTID(thread_id); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t +Breakpoint::GetThreadID () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return LLDB_INVALID_THREAD_ID; + else + return m_options.GetThreadSpecNoCreate()->GetTID(); +} + +void +Breakpoint::SetThreadIndex (uint32_t index) +{ + if (m_options.GetThreadSpec()->GetIndex() == index) + return; + + m_options.GetThreadSpec()->SetIndex(index); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +uint32_t +Breakpoint::GetThreadIndex() const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return 0; + else + return m_options.GetThreadSpecNoCreate()->GetIndex(); +} + +void +Breakpoint::SetThreadName (const char *thread_name) +{ + if (m_options.GetThreadSpec()->GetName() != NULL + && ::strcmp (m_options.GetThreadSpec()->GetName(), thread_name) == 0) + return; + + m_options.GetThreadSpec()->SetName (thread_name); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +Breakpoint::GetThreadName () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return NULL; + else + return m_options.GetThreadSpecNoCreate()->GetName(); +} + +void +Breakpoint::SetQueueName (const char *queue_name) +{ + if (m_options.GetThreadSpec()->GetQueueName() != NULL + && ::strcmp (m_options.GetThreadSpec()->GetQueueName(), queue_name) == 0) + return; + + m_options.GetThreadSpec()->SetQueueName (queue_name); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +Breakpoint::GetQueueName () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return NULL; + else + return m_options.GetThreadSpecNoCreate()->GetQueueName(); +} + +void +Breakpoint::SetCondition (const char *condition) +{ + m_options.SetCondition (condition); + SendBreakpointChangedEvent (eBreakpointEventTypeConditionChanged); +} + +const char * +Breakpoint::GetConditionText () const +{ + return m_options.GetConditionText(); +} + +// This function is used when "baton" doesn't need to be freed +void +Breakpoint::SetCallback (BreakpointHitCallback callback, void *baton, bool is_synchronous) +{ + // The default "Baton" class will keep a copy of "baton" and won't free + // or delete it when it goes goes out of scope. + m_options.SetCallback(callback, BatonSP (new Baton(baton)), is_synchronous); + + SendBreakpointChangedEvent (eBreakpointEventTypeCommandChanged); +} + +// This function is used when a baton needs to be freed and therefore is +// contained in a "Baton" subclass. +void +Breakpoint::SetCallback (BreakpointHitCallback callback, const BatonSP &callback_baton_sp, bool is_synchronous) +{ + m_options.SetCallback(callback, callback_baton_sp, is_synchronous); +} + +void +Breakpoint::ClearCallback () +{ + m_options.ClearCallback (); +} + +bool +Breakpoint::InvokeCallback (StoppointCallbackContext *context, break_id_t bp_loc_id) +{ + return m_options.InvokeCallback (context, GetID(), bp_loc_id); +} + +BreakpointOptions * +Breakpoint::GetOptions () +{ + return &m_options; +} + +void +Breakpoint::ResolveBreakpoint () +{ + if (m_resolver_sp) + m_resolver_sp->ResolveBreakpoint(*m_filter_sp); +} + +void +Breakpoint::ResolveBreakpointInModules (ModuleList &module_list) +{ + if (m_resolver_sp) + 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) +{ + Mutex::Locker modules_mutex(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. + + const size_t num_locs = m_locations.GetSize(); + size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; i++) + { + bool seen = false; + ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (i)); + if (!m_filter_sp->ModulePasses (module_sp)) + continue; + + for (size_t loc_idx = 0; loc_idx < num_locs; loc_idx++) + { + BreakpointLocationSP break_loc = m_locations.GetByIndex(loc_idx); + if (!break_loc->IsEnabled()) + continue; + SectionSP section_sp (break_loc->GetAddress().GetSection()); + if (!section_sp || section_sp->GetModule() == module_sp) + { + if (!seen) + seen = true; + + if (!break_loc->ResolveBreakpointSite()) + { + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("Warning: could not set breakpoint site for breakpoint location %d of breakpoint %d.\n", + break_loc->GetID(), GetID()); + } + } + } + + if (!seen) + new_modules.AppendIfNeeded (module_sp); + + } + + if (new_modules.GetSize() > 0) + { + // If this is not an internal breakpoint, set up to record the new locations, then dispatch + // an event with the new locations. + if (!IsInternal()) + { + BreakpointEventData *new_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded, + shared_from_this()); + + m_locations.StartRecordingNewLocations(new_locations_event->GetBreakpointLocationCollection()); + + ResolveBreakpointInModules(new_modules); + + m_locations.StopRecordingNewLocations(); + if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0) + { + SendBreakpointChangedEvent (new_locations_event); + } + else + delete new_locations_event; + } + else + 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 = NULL; + + 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); + } +} + +void +Breakpoint::ModuleReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp) +{ + ModuleList temp_list; + temp_list.Append (new_module_sp); + ModulesChanged (temp_list, true); + + // TO DO: For now I'm just adding locations for the new module and removing the + // breakpoint locations that were in the old module. + // We should really go find the ones that are in the new module & if we can determine that they are "equivalent" + // carry over the options from the old location to the new. + + temp_list.Clear(); + temp_list.Append (old_module_sp); + ModulesChanged (temp_list, false, true); +} + +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(); +} + +size_t +Breakpoint::GetNumLocations() const +{ + return m_locations.GetSize(); +} + +void +Breakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_locations) +{ + assert (s != NULL); + + if (!m_kind_description.empty()) + { + if (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, (uint64_t)num_resolved_locations); + } + 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 (level == lldb::eDescriptionLevelFull) + { + 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) + { + // If there is one location only, we'll just print that location information. But don't do this if + // show locations is true, then that will be handled below. + if (show_locations == false) + { + GetLocationAtIndex(0)->GetDescription(s, level); + } + else + { + s->Printf ("%zd locations.", num_locations); + } + } + else + { + s->Printf ("%zd locations.", 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 (const 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); +} + +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 == NULL) + 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 () +{ +} + +const ConstString & +Breakpoint::BreakpointEventData::GetFlavorString () +{ + static ConstString g_flavor ("Breakpoint::BreakpointEventData"); + return g_flavor; +} + +const 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 NULL; +} + +BreakpointEventType +Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (const EventSP &event_sp) +{ + const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get()); + + if (data == NULL) + 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/source/Breakpoint/BreakpointID.cpp b/source/Breakpoint/BreakpointID.cpp new file mode 100644 index 000000000000..9a59e29d007d --- /dev/null +++ b/source/Breakpoint/BreakpointID.cpp @@ -0,0 +1,123 @@ +//===-- BreakpointID.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +#include <stdio.h> + +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/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 () +{ +} + +const char *BreakpointID::g_range_specifiers[] = { "-", "to", "To", "TO", NULL }; + +// 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 (const char *str) +{ + int specifier_count = 0; + for (int i = 0; g_range_specifiers[i] != NULL; ++i) + ++specifier_count; + + for (int i = 0; i < specifier_count; ++i) + { + if (strcmp (g_range_specifiers[i], str) == 0) + return true; + } + + return false; +} + +bool +BreakpointID::IsValidIDExpression (const char *str) +{ + break_id_t bp_id; + break_id_t loc_id; + BreakpointID::ParseCanonicalReference (str, &bp_id, &loc_id); + + if (bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +void +BreakpointID::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == eDescriptionLevelVerbose) + s->Printf("%p BreakpointID:", 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); +} + +bool +BreakpointID::ParseCanonicalReference (const char *input, break_id_t *break_id_ptr, break_id_t *break_loc_id_ptr) +{ + *break_id_ptr = LLDB_INVALID_BREAK_ID; + *break_loc_id_ptr = LLDB_INVALID_BREAK_ID; + + if (input == NULL || *input == '\0') + return false; + + const char *format = "%i%n.%i%n"; + int chars_consumed_1 = 0; + int chars_consumed_2 = 0; + int n_items_parsed = ::sscanf (input, + format, + break_id_ptr, // %i parse the breakpoint ID + &chars_consumed_1, // %n gets the number of characters parsed so far + break_loc_id_ptr, // %i parse the breakpoint location ID + &chars_consumed_2); // %n gets the number of characters parsed so far + + if ((n_items_parsed == 1 && input[chars_consumed_1] == '\0') || + (n_items_parsed == 2 && input[chars_consumed_2] == '\0')) + return true; + + // Badly formatted canonical reference. + *break_id_ptr = LLDB_INVALID_BREAK_ID; + *break_loc_id_ptr = LLDB_INVALID_BREAK_ID; + return false; +} + diff --git a/source/Breakpoint/BreakpointIDList.cpp b/source/Breakpoint/BreakpointIDList.cpp new file mode 100644 index 000000000000..24101b1442fb --- /dev/null +++ b/source/Breakpoint/BreakpointIDList.cpp @@ -0,0 +1,397 @@ +//===-- BreakpointIDList.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointIDList.h" + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/Target.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 () +{ +} + +size_t +BreakpointIDList::GetSize() +{ + return m_breakpoint_ids.size(); +} + +BreakpointID & +BreakpointIDList::GetBreakpointIDAtIndex (size_t index) +{ + if (index < m_breakpoint_ids.size()) + return m_breakpoint_ids[index]; + else + return 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) +{ + BreakpointID temp_bp_id; + break_id_t bp_id; + break_id_t loc_id; + + bool success = BreakpointID::ParseCanonicalReference (bp_id_str, &bp_id, &loc_id); + + if (success) + { + temp_bp_id.SetID (bp_id, loc_id); + m_breakpoint_ids.push_back (temp_bp_id); + } + + return success; +} + +bool +BreakpointIDList::FindBreakpointID (BreakpointID &bp_id, size_t *position) +{ + 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) +{ + BreakpointID temp_bp_id; + break_id_t bp_id; + break_id_t loc_id; + + if (BreakpointID::ParseCanonicalReference (bp_id_str, &bp_id, &loc_id)) + { + temp_bp_id.SetID (bp_id, loc_id); + return FindBreakpointID (temp_bp_id, position); + } + else + return false; +} + +void +BreakpointIDList::InsertStringArray (const char **string_array, size_t array_size, CommandReturnObject &result) +{ + if (string_array == NULL) + return; + + for (uint32_t i = 0; i < array_size; ++i) + { + break_id_t bp_id; + break_id_t loc_id; + + if (BreakpointID::ParseCanonicalReference (string_array[i], &bp_id, &loc_id)) + { + if (bp_id != LLDB_INVALID_BREAK_ID) + { + BreakpointID temp_bp_id(bp_id, loc_id); + m_breakpoint_ids.push_back (temp_bp_id); + } + else + { + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", string_array[i]); + result.SetStatus (eReturnStatusFailed); + return; + } + } + } + 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, CommandReturnObject &result, + Args &new_args) +{ + std::string range_start; + const char *range_end; + const char *current_arg; + const size_t num_old_args = old_args.GetArgumentCount(); + + for (size_t i = 0; i < num_old_args; ++i) + { + bool is_range = false; + current_arg = old_args.GetArgumentAtIndex (i); + + size_t range_start_len = 0; + size_t range_end_pos = 0; + if (BreakpointIDList::StringContainsIDRangeExpression (current_arg, &range_start_len, &range_end_pos)) + { + is_range = true; + range_start.assign (current_arg, range_start_len); + range_end = current_arg + range_end_pos; + } + else if ((i + 2 < num_old_args) + && BreakpointID::IsRangeIdentifier (old_args.GetArgumentAtIndex (i+1)) + && BreakpointID::IsValidIDExpression (current_arg) + && BreakpointID::IsValidIDExpression (old_args.GetArgumentAtIndex (i+2))) + { + range_start.assign (current_arg); + range_end = old_args.GetArgumentAtIndex (i+2); + is_range = true; + i = i+2; + } + else + { + // See if user has specified id.* + std::string tmp_str = old_args.GetArgumentAtIndex (i); + size_t pos = tmp_str.find ('.'); + if (pos != std::string::npos) + { + std::string bp_id_str = tmp_str.substr (0, pos); + if (BreakpointID::IsValidIDExpression (bp_id_str.c_str()) + && tmp_str[pos+1] == '*' + && tmp_str.length() == (pos + 2)) + { + break_id_t bp_id; + break_id_t bp_loc_id; + + BreakpointID::ParseCanonicalReference (bp_id_str.c_str(), &bp_id, &bp_loc_id); + BreakpointSP breakpoint_sp = target->GetBreakpointByID (bp_id); + if (! breakpoint_sp) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%d' is not a valid breakpoint ID.\n", bp_id); + 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, bp_loc->GetID()); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + + } + } + + if (is_range) + { + break_id_t start_bp_id; + break_id_t end_bp_id; + break_id_t start_loc_id; + break_id_t end_loc_id; + + BreakpointID::ParseCanonicalReference (range_start.c_str(), &start_bp_id, &start_loc_id); + BreakpointID::ParseCanonicalReference (range_end, &end_bp_id, &end_loc_id); + + if ((start_bp_id == LLDB_INVALID_BREAK_ID) + || (! target->GetBreakpointByID (start_bp_id))) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_start.c_str()); + result.SetStatus (eReturnStatusFailed); + return; + } + + if ((end_bp_id == LLDB_INVALID_BREAK_ID) + || (! target->GetBreakpointByID (end_bp_id))) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_end); + result.SetStatus (eReturnStatusFailed); + return; + } + + + 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.GetData()); + } + } + } + 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.GetData()); + } + } + } + else + { + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, LLDB_INVALID_BREAK_ID); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + } + else // else is_range was false + { + new_args.AppendArgument (current_arg); + } + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return; +} + +bool +BreakpointIDList::StringContainsIDRangeExpression (const char *in_string, + size_t *range_start_len, + size_t *range_end_pos) +{ + bool is_range_expression = false; + std::string arg_str = in_string; + std::string::size_type idx; + std::string::size_type start_pos = 0; + + *range_start_len = 0; + *range_end_pos = 0; + + int specifiers_size = 0; + for (int i = 0; BreakpointID::g_range_specifiers[i] != NULL; ++i) + ++specifiers_size; + + for (int i = 0; i < specifiers_size && !is_range_expression; ++i) + { + const char *specifier_str = BreakpointID::g_range_specifiers[i]; + size_t len = strlen (specifier_str); + idx = arg_str.find (BreakpointID::g_range_specifiers[i]); + if (idx != std::string::npos) + { + *range_start_len = idx - start_pos; + std::string start_str = arg_str.substr (start_pos, *range_start_len); + if (idx + len < arg_str.length()) + { + *range_end_pos = idx + len; + std::string end_str = arg_str.substr (*range_end_pos); + if (BreakpointID::IsValidIDExpression (start_str.c_str()) + && BreakpointID::IsValidIDExpression (end_str.c_str())) + { + is_range_expression = true; + //*range_start = start_str; + //*range_end = end_str; + } + } + } + } + + if (!is_range_expression) + { + *range_start_len = 0; + *range_end_pos = 0; + } + + return is_range_expression; +} diff --git a/source/Breakpoint/BreakpointList.cpp b/source/Breakpoint/BreakpointList.cpp new file mode 100644 index 000000000000..5926663af7b1 --- /dev/null +++ b/source/Breakpoint/BreakpointList.cpp @@ -0,0 +1,243 @@ +//===-- BreakpointList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointList::BreakpointList (bool is_internal) : + m_mutex (Mutex::eMutexTypeRecursive), + m_breakpoints(), + m_next_break_id (0), + m_is_internal (is_internal) +{ +} + +BreakpointList::~BreakpointList() +{ +} + + +break_id_t +BreakpointList::Add (BreakpointSP &bp_sp, bool notify) +{ + Mutex::Locker locker(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) + { + if (bp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + bp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeAdded, bp_sp)); + } + return bp_sp->GetID(); +} + +bool +BreakpointList::Remove (break_id_t break_id, bool notify) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator pos = GetBreakpointIDIterator(break_id); // Predicate + if (pos != m_breakpoints.end()) + { + BreakpointSP bp_sp (*pos); + m_breakpoints.erase(pos); + if (notify) + { + if (bp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + bp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeRemoved, bp_sp)); + } + return true; + } + return false; +} + +void +BreakpointList::SetEnabledAll (bool enabled) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->SetEnabled (enabled); +} + + +void +BreakpointList::RemoveAll (bool notify) +{ + Mutex::Locker locker(m_mutex); + ClearAllBreakpointSites (); + + if (notify) + { + bp_collection::iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + { + if ((*pos)->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + { + (*pos)->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeRemoved, + *pos)); + } + } + } + m_breakpoints.erase (m_breakpoints.begin(), m_breakpoints.end()); +} + +class BreakpointIDMatches +{ +public: + BreakpointIDMatches (break_id_t break_id) : + m_break_id(break_id) + { + } + + bool operator() (const BreakpointSP &bp) const + { + return m_break_id == bp->GetID(); + } + +private: + const break_id_t m_break_id; +}; + +BreakpointList::bp_collection::iterator +BreakpointList::GetBreakpointIDIterator (break_id_t break_id) +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(break_id)); // Predicate +} + +BreakpointList::bp_collection::const_iterator +BreakpointList::GetBreakpointIDConstIterator (break_id_t break_id) const +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(break_id)); // Predicate +} + +BreakpointSP +BreakpointList::FindBreakpointByID (break_id_t break_id) +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::iterator pos = GetBreakpointIDIterator(break_id); + if (pos != m_breakpoints.end()) + stop_sp = *pos; + + return stop_sp; +} + +const BreakpointSP +BreakpointList::FindBreakpointByID (break_id_t break_id) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::const_iterator pos = GetBreakpointIDConstIterator(break_id); + if (pos != m_breakpoints.end()) + stop_sp = *pos; + + return stop_sp; +} + +void +BreakpointList::Dump (Stream *s) const +{ + Mutex::Locker locker(m_mutex); + s->Printf("%p: ", this); + s->Indent(); + s->Printf("BreakpointList with %u Breakpoints:\n", (uint32_t)m_breakpoints.size()); + s->IndentMore(); + bp_collection::const_iterator pos; + bp_collection::const_iterator end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->Dump(s); + s->IndentLess(); +} + + +BreakpointSP +BreakpointList::GetBreakpointAtIndex (size_t i) +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + size_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = *pos; + } + return stop_sp; +} + +const BreakpointSP +BreakpointList::GetBreakpointAtIndex (size_t i) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::const_iterator end = m_breakpoints.end(); + bp_collection::const_iterator pos; + size_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = *pos; + } + return stop_sp; +} + +void +BreakpointList::UpdateBreakpoints (ModuleList& module_list, bool added) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ModulesChanged (module_list, added); + +} + +void +BreakpointList::UpdateBreakpointsWhenModuleIsReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ModuleReplaced (old_module_sp, new_module_sp); + +} + +void +BreakpointList::ClearAllBreakpointSites () +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ClearAllBreakpointSites (); + +} + +void +BreakpointList::GetListMutex (Mutex::Locker &locker) +{ + return locker.Lock (m_mutex); +} diff --git a/source/Breakpoint/BreakpointLocation.cpp b/source/Breakpoint/BreakpointLocation.cpp new file mode 100644 index 000000000000..1ec726dd52b1 --- /dev/null +++ b/source/Breakpoint/BreakpointLocation.cpp @@ -0,0 +1,677 @@ +//===-- BreakpointLocation.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +// C++ Includes +#include <string> + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.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 +) : + StoppointLocation (loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), hardware), + m_being_created(true), + m_address (addr), + m_owner (owner), + m_options_ap (), + m_bp_site_sp (), + m_condition_mutex () +{ + SetThreadID (tid); + m_being_created = false; +} + +BreakpointLocation::~BreakpointLocation() +{ + ClearBreakpointSite(); +} + +lldb::addr_t +BreakpointLocation::GetLoadAddress () const +{ + return m_address.GetOpcodeLoadAddress (&m_owner.GetTarget()); +} + +Address & +BreakpointLocation::GetAddress () +{ + return m_address; +} + +Breakpoint & +BreakpointLocation::GetBreakpoint () +{ + return m_owner; +} + +bool +BreakpointLocation::IsEnabled () const +{ + if (!m_owner.IsEnabled()) + return false; + else if (m_options_ap.get() != NULL) + return m_options_ap->IsEnabled(); + else + return true; +} + +void +BreakpointLocation::SetEnabled (bool enabled) +{ + GetLocationOptions()->SetEnabled(enabled); + if (enabled) + { + ResolveBreakpointSite(); + } + else + { + ClearBreakpointSite(); + } + SendBreakpointLocationChangedEvent (enabled ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled); +} + +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_ap.get() != NULL) + m_options_ap->SetThreadID (thread_id); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t +BreakpointLocation::GetThreadID () +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->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_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetIndex(index); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); + +} + +uint32_t +BreakpointLocation::GetThreadIndex() const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetIndex(); + else + return 0; +} + +void +BreakpointLocation::SetThreadName (const char *thread_name) +{ + if (thread_name != NULL) + 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_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetName(thread_name); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +BreakpointLocation::GetThreadName () const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetName(); + else + return NULL; +} + +void +BreakpointLocation::SetQueueName (const char *queue_name) +{ + if (queue_name != NULL) + 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_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetQueueName(queue_name); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +BreakpointLocation::GetQueueName () const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetQueueName(); + else + return NULL; +} + +bool +BreakpointLocation::InvokeCallback (StoppointCallbackContext *context) +{ + if (m_options_ap.get() != NULL && m_options_ap->HasCallback()) + return m_options_ap->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, BatonSP (new Baton(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 GetOptionsNoCreate()->GetConditionText(hash); +} + +bool +BreakpointLocation::ConditionSaysStop (ExecutionContext &exe_ctx, Error &error) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + + Mutex::Locker evaluation_locker(m_condition_mutex); + + size_t condition_hash; + const char *condition_text = GetConditionText(&condition_hash); + + if (!condition_text) + { + m_user_expression_sp.reset(); + return false; + } + + if (condition_hash != m_condition_hash || + !m_user_expression_sp || + !m_user_expression_sp->MatchesContext(exe_ctx)) + { + m_user_expression_sp.reset(new ClangUserExpression(condition_text, + NULL, + lldb::eLanguageTypeUnknown, + ClangUserExpression::eResultTypeAny)); + + StreamString errors; + + if (!m_user_expression_sp->Parse(errors, + exe_ctx, + eExecutionPolicyOnlyWhenNeeded, + true)) + { + error.SetErrorStringWithFormat("Couldn't parse conditional expression:\n%s", + errors.GetData()); + m_user_expression_sp.reset(); + return false; + } + + 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; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + const bool try_all_threads = true; + + Error expr_error; + + StreamString execution_errors; + + ClangExpressionVariableSP result_variable_sp; + + ExecutionResults result_code = + m_user_expression_sp->Execute(execution_errors, + exe_ctx, + unwind_on_error, + ignore_breakpoints, + m_user_expression_sp, + result_variable_sp, + try_all_threads, + ClangUserExpression::kDefaultTimeout); + + bool ret; + + if (result_code == eExecutionCompleted) + { + if (!result_variable_sp) + { + ret = false; + error.SetErrorString("Expression did not return a result"); + return false; + } + + result_value_sp = result_variable_sp->GetValueObject(); + + if (result_value_sp) + { + Scalar scalar_value; + if (result_value_sp->ResolveValue (scalar_value)) + { + if (scalar_value.ULongLong(1) == 0) + ret = false; + else + ret = true; + if (log) + log->Printf("Condition successfully evaluated, result is %s.\n", + ret ? "true" : "false"); + } + else + { + ret = false; + error.SetErrorString("Failed to get an integer result from the expression"); + } + } + else + { + ret = false; + error.SetErrorString("Failed to get any result from the expression"); + } + } + else + { + ret = false; + error.SetErrorStringWithFormat("Couldn't execute expression:\n%s", execution_errors.GetData()); + } + + return ret; +} + +uint32_t +BreakpointLocation::GetIgnoreCount () +{ + return GetOptionsNoCreate()->GetIgnoreCount(); +} + +void +BreakpointLocation::SetIgnoreCount (uint32_t n) +{ + GetLocationOptions()->SetIgnoreCount(n); + SendBreakpointLocationChangedEvent (eBreakpointEventTypeIgnoreChanged); +} + +void +BreakpointLocation::DecrementIgnoreCount() +{ + if (m_options_ap.get() != NULL) + { + uint32_t loc_ignore = m_options_ap->GetIgnoreCount(); + if (loc_ignore != 0) + m_options_ap->SetIgnoreCount(loc_ignore - 1); + } +} + +bool +BreakpointLocation::IgnoreCountShouldStop() +{ + if (m_options_ap.get() != NULL) + { + uint32_t loc_ignore = m_options_ap->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; +} + +const BreakpointOptions * +BreakpointLocation::GetOptionsNoCreate () const +{ + if (m_options_ap.get() != NULL) + return m_options_ap.get(); + else + return m_owner.GetOptions (); +} + +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_ap.get() == NULL) + m_options_ap.reset(BreakpointOptions::CopyOptionsNoCallback(*m_owner.GetOptions ())); + + return m_options_ap.get(); +} + +bool +BreakpointLocation::ValidForThisThread (Thread *thread) +{ + return thread->MatchesSpec(GetOptionsNoCreate()->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); + + IncrementHitCount(); + + 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); + log->Printf ("Hit breakpoint location: %s, %s.\n", s.GetData(), should_stop ? "stopping" : "continuing"); + } + + return should_stop; +} + +bool +BreakpointLocation::IsResolved () const +{ + return m_bp_site_sp.get() != NULL; +} + +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 == NULL) + return false; + + lldb::break_id_t new_id = process->CreateBreakpointSite (shared_from_this(), false); + + if (new_id == LLDB_INVALID_BREAK_ID) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Warning ("Tried to add breakpoint site at 0x%" PRIx64 " but it was already present.\n", + m_address.GetOpcodeLoadAddress (&m_owner.GetTarget())); + return false; + } + + return true; +} + +bool +BreakpointLocation::SetBreakpointSite (BreakpointSiteSP& bp_site_sp) +{ + m_bp_site_sp = bp_site_sp; + return true; +} + +bool +BreakpointLocation::ClearBreakpointSite () +{ + if (m_bp_site_sp.get()) + { + m_owner.GetTarget().GetProcessSP()->RemoveOwnerFromBreakpointSite (GetBreakpoint().GetID(), + GetID(), m_bp_site_sp); + 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) + { + s->PutCString("where = "); + sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); + } + else + { + if (sc.module_sp) + { + s->EOL(); + s->Indent("module = "); + sc.module_sp->GetFileSpec().Dump (s); + } + + if (sc.comp_unit != NULL) + { + s->EOL(); + s->Indent("compile unit = "); + static_cast<FileSpec*>(sc.comp_unit)->GetFilename().Dump (s); + + if (sc.function != NULL) + { + s->EOL(); + s->Indent("function = "); + s->PutCString (sc.function->GetMangled().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(); + s->Indent("symbol = "); + s->PutCString(sc.symbol->GetMangled().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 = NULL; + Target *target = &m_owner.GetTarget(); + if (target) + exe_scope = target->GetProcessSP().get(); + if (exe_scope == NULL) + exe_scope = target; + + if (eDescriptionLevelInitial) + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + else + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); + + 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_ap.get()) + { + s->Indent(); + m_options_ap->GetDescription (s, level); + s->EOL(); + } + s->IndentLess(); + } + else if (level != eDescriptionLevelInitial) + { + s->Printf(", %sresolved, hit count = %u ", + (IsResolved() ? "" : "un"), + GetHitCount()); + if (m_options_ap.get()) + { + m_options_ap->GetDescription (s, level); + } + } +} + +void +BreakpointLocation::Dump(Stream *s) const +{ + if (s == NULL) + return; + + 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(), + GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetTID(), + (uint64_t) m_address.GetOpcodeLoadAddress (&m_owner.GetTarget()), + (m_options_ap.get() ? m_options_ap->IsEnabled() : m_owner.IsEnabled()) ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + GetHardwareIndex(), + GetHitCount(), + GetOptionsNoCreate()->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); + } +} + diff --git a/source/Breakpoint/BreakpointLocationCollection.cpp b/source/Breakpoint/BreakpointLocationCollection.cpp new file mode 100644 index 000000000000..ee3f56f928d5 --- /dev/null +++ b/source/Breakpoint/BreakpointLocationCollection.cpp @@ -0,0 +1,198 @@ +//===-- BreakpointLocationCollection.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.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() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +BreakpointLocationCollection::~BreakpointLocationCollection() +{ +} + +void +BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) +{ + 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) +{ + 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) +{ + 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 +{ + 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; + const size_t count = GetSize(); + for (size_t i = 0; i < count; i++) + { + if (GetByIndex(i)->ShouldStop(context)) + shouldStop = true; + } + return shouldStop; +} + +bool +BreakpointLocationCollection::ValidForThisThread (Thread *thread) +{ + 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 +{ + 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) +{ + 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); + } +} diff --git a/source/Breakpoint/BreakpointLocationList.cpp b/source/Breakpoint/BreakpointLocationList.cpp new file mode 100644 index 000000000000..22a4ff0c68ee --- /dev/null +++ b/source/Breakpoint/BreakpointLocationList.cpp @@ -0,0 +1,305 @@ +//===-- BreakpointLocationList.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationList.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointLocationList::BreakpointLocationList(Breakpoint &owner) : + m_owner (owner), + m_locations(), + m_address_to_location (), + m_mutex (Mutex::eMutexTypeRecursive), + m_next_id (0), + m_new_location_recorder (NULL) +{ +} + +BreakpointLocationList::~BreakpointLocationList() +{ +} + +BreakpointLocationSP +BreakpointLocationList::Create (const Address &addr) +{ + Mutex::Locker locker (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)); + 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; +} + +BreakpointLocationSP +BreakpointLocationList::FindByID (lldb::break_id_t break_id) const +{ + BreakpointLocationSP bp_loc_sp; + Mutex::Locker locker (m_mutex); + // We never remove a breakpoint locations, so the ID can be translated into + // the location index by subtracting 1 + uint32_t idx = break_id - 1; + if (idx <= m_locations.size()) + { + bp_loc_sp = m_locations[idx]; + } + return bp_loc_sp; +} + +size_t +BreakpointLocationList::FindInModule (Module *module, + BreakpointLocationCollection& bp_loc_list) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (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: ", this); + //s->Indent(); + Mutex::Locker locker (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).get()->Dump(s); + s->IndentLess(); +} + + +BreakpointLocationSP +BreakpointLocationList::GetByIndex (size_t i) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP bp_loc_sp; + if (i < m_locations.size()) + bp_loc_sp = m_locations[i]; + + return bp_loc_sp; +} + +void +BreakpointLocationList::ClearAllBreakpointSites () +{ + Mutex::Locker locker (m_mutex); + collection::iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + (*pos)->ClearBreakpointSite(); +} + +void +BreakpointLocationList::ResolveAllBreakpointSites () +{ + Mutex::Locker locker (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; + Mutex::Locker locker (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 +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker (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 *new_location) +{ + Mutex::Locker locker (m_mutex); + + if (new_location) + *new_location = false; + BreakpointLocationSP bp_loc_sp (FindByAddress(addr)); + if (!bp_loc_sp) + { + bp_loc_sp = Create (addr); + 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; +} + +bool +BreakpointLocationList::RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp) +{ + if (bp_loc_sp) + { + Mutex::Locker locker (m_mutex); + + m_address_to_location.erase (bp_loc_sp->GetAddress()); + + collection::iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + { + if ((*pos).get() == bp_loc_sp.get()) + { + m_locations.erase (pos); + return true; + } + } + } + return false; +} + + + +void +BreakpointLocationList::StartRecordingNewLocations (BreakpointLocationCollection &new_locations) +{ + Mutex::Locker locker (m_mutex); + assert (m_new_location_recorder == NULL); + m_new_location_recorder = &new_locations; +} + +void +BreakpointLocationList::StopRecordingNewLocations () +{ + Mutex::Locker locker (m_mutex); + m_new_location_recorder = NULL; +} + diff --git a/source/Breakpoint/BreakpointOptions.cpp b/source/Breakpoint/BreakpointOptions.cpp new file mode 100644 index 000000000000..3a4a117695fc --- /dev/null +++ b/source/Breakpoint/BreakpointOptions.cpp @@ -0,0 +1,298 @@ +//===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointOptions.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Value.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.h" + +using namespace lldb; +using namespace lldb_private; + +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() : + m_callback (BreakpointOptions::NullCallback), + m_callback_baton_sp (), + m_callback_is_synchronous (false), + m_enabled (true), + m_one_shot (false), + m_ignore_count (0), + m_thread_spec_ap (), + m_condition_text (), + m_condition_text_hash (0) +{ +} + +//---------------------------------------------------------------------- +// BreakpointOptions copy constructor +//---------------------------------------------------------------------- +BreakpointOptions::BreakpointOptions(const BreakpointOptions& 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_enabled (rhs.m_enabled), + m_one_shot (rhs.m_one_shot), + m_ignore_count (rhs.m_ignore_count), + m_thread_spec_ap () +{ + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get())); + 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_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_ap.get() != NULL) + m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); + m_condition_text = rhs.m_condition_text; + m_condition_text_hash = rhs.m_condition_text_hash; + return *this; +} + +BreakpointOptions * +BreakpointOptions::CopyOptionsNoCallback (BreakpointOptions &orig) +{ + BreakpointHitCallback 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(); + BreakpointOptions *ret_val = new BreakpointOptions(orig); + + orig.SetCallback (orig_callback, orig_callback_baton_sp, orig_is_sync); + + return ret_val; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +BreakpointOptions::~BreakpointOptions() +{ +} + +//------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------ +void +BreakpointOptions::SetCallback (BreakpointHitCallback 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 +BreakpointOptions::ClearCallback () +{ + m_callback = BreakpointOptions::NullCallback; + m_callback_is_synchronous = false; + m_callback_baton_sp.reset(); +} + +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 && context->is_synchronous == IsCallbackSynchronous()) + { + return m_callback (m_callback_baton_sp ? m_callback_baton_sp->m_data : NULL, + context, + break_id, + break_loc_id); + } + else + return true; +} + +bool +BreakpointOptions::HasCallback () +{ + return m_callback != BreakpointOptions::NullCallback; +} + +void +BreakpointOptions::SetCondition (const char *condition) +{ + if (!condition) + condition = ""; + + 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 NULL; + } +} + +const ThreadSpec * +BreakpointOptions::GetThreadSpecNoCreate () const +{ + return m_thread_spec_ap.get(); +} + +ThreadSpec * +BreakpointOptions::GetThreadSpec () +{ + if (m_thread_spec_ap.get() == NULL) + m_thread_spec_ap.reset (new ThreadSpec()); + + return m_thread_spec_ap.get(); +} + +void +BreakpointOptions::SetThreadID (lldb::tid_t thread_id) +{ + GetThreadSpec()->SetTID(thread_id); +} + +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 || (GetThreadSpecNoCreate() != NULL && 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_thread_spec_ap.get()) + m_thread_spec_ap->GetDescription (s, level); + else if (level == eDescriptionLevelBrief) + s->PutCString ("thread spec: no "); + 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, level); + } + } + if (!m_condition_text.empty()) + { + if (level != eDescriptionLevelBrief) + { + s->EOL(); + s->Printf("Condition: %s\n", m_condition_text.c_str()); + } + } +} + +void +BreakpointOptions::CommandBaton::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ + CommandData *data = (CommandData *)m_data; + + if (level == eDescriptionLevelBrief) + { + s->Printf (", commands = %s", (data && data->user_source.GetSize() > 0) ? "yes" : "no"); + return; + } + + s->IndentMore (); + s->Indent("Breakpoint commands:\n"); + + s->IndentMore (); + if (data && data->user_source.GetSize() > 0) + { + const size_t num_strings = data->user_source.GetSize(); + for (size_t i = 0; i < num_strings; ++i) + { + s->Indent(data->user_source.GetStringAtIndex(i)); + s->EOL(); + } + } + else + { + s->PutCString ("No commands.\n"); + } + s->IndentLess (); + s->IndentLess (); +} + diff --git a/source/Breakpoint/BreakpointResolver.cpp b/source/Breakpoint/BreakpointResolver.cpp new file mode 100644 index 000000000000..b22fa1e6dbcc --- /dev/null +++ b/source/Breakpoint/BreakpointResolver.cpp @@ -0,0 +1,61 @@ +//===-- BreakpointResolver.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolver.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolver: +//---------------------------------------------------------------------- +BreakpointResolver::BreakpointResolver (Breakpoint *bkpt, const unsigned char resolverTy) : + m_breakpoint (bkpt), + SubclassID (resolverTy) +{ +} + +BreakpointResolver::~BreakpointResolver () +{ + +} + +void +BreakpointResolver::SetBreakpoint (Breakpoint *bkpt) +{ + m_breakpoint = bkpt; +} + +void +BreakpointResolver::ResolveBreakpointInModules (SearchFilter &filter, ModuleList &modules) +{ + filter.SearchInModuleList(*this, modules); +} + +void +BreakpointResolver::ResolveBreakpoint (SearchFilter &filter) +{ + filter.Search (*this); +} + diff --git a/source/Breakpoint/BreakpointResolverAddress.cpp b/source/Breakpoint/BreakpointResolverAddress.cpp new file mode 100644 index 000000000000..1bcef93aedad --- /dev/null +++ b/source/Breakpoint/BreakpointResolverAddress.cpp @@ -0,0 +1,111 @@ +//===-- BreakpointResolverAddress.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverAddress.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverAddress: +//---------------------------------------------------------------------- +BreakpointResolverAddress::BreakpointResolverAddress +( + Breakpoint *bkpt, + const Address &addr +) : + BreakpointResolver (bkpt, BreakpointResolver::AddressResolver), + m_addr (addr) +{ +} + +BreakpointResolverAddress::~BreakpointResolverAddress () +{ + +} + +void +BreakpointResolverAddress::ResolveBreakpoint (SearchFilter &filter) +{ + // The address breakpoint only takes once, so if we've already set it we're done. + if (m_breakpoint->GetNumLocations() > 0) + return; + else + BreakpointResolver::ResolveBreakpoint(filter); +} + +void +BreakpointResolverAddress::ResolveBreakpointInModules +( + SearchFilter &filter, + ModuleList &modules +) +{ + // The address breakpoint only takes once, so if we've already set it we're done. + if (m_breakpoint->GetNumLocations() > 0) + return; + else + BreakpointResolver::ResolveBreakpointInModules (filter, modules); +} + +Searcher::CallbackReturn +BreakpointResolverAddress::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + assert (m_breakpoint != NULL); + + if (filter.AddressPasses (m_addr)) + { + BreakpointLocationSP bp_loc_sp(m_breakpoint->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)); + if (log) + log->Printf ("Added location: %s\n", s.GetData()); + } + } + return Searcher::eCallbackReturnStop; +} + +Searcher::Depth +BreakpointResolverAddress::GetDepth() +{ + return Searcher::eDepthTarget; +} + +void +BreakpointResolverAddress::GetDescription (Stream *s) +{ + s->PutCString ("address = "); + m_addr.Dump(s, m_breakpoint->GetTarget().GetProcessSP().get(), Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); +} + +void +BreakpointResolverAddress::Dump (Stream *s) const +{ + +} diff --git a/source/Breakpoint/BreakpointResolverFileLine.cpp b/source/Breakpoint/BreakpointResolverFileLine.cpp new file mode 100644 index 000000000000..91a218fdb80a --- /dev/null +++ b/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -0,0 +1,246 @@ +//===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverFileLine: +//---------------------------------------------------------------------- +BreakpointResolverFileLine::BreakpointResolverFileLine +( + Breakpoint *bkpt, + const FileSpec &file_spec, + uint32_t line_no, + bool check_inlines, + bool skip_prologue +) : + BreakpointResolver (bkpt, BreakpointResolver::FileLineResolver), + m_file_spec (file_spec), + m_line_number (line_no), + m_inlines (check_inlines), + m_skip_prologue(skip_prologue) +{ +} + +BreakpointResolverFileLine::~BreakpointResolverFileLine () +{ +} + +Searcher::CallbackReturn +BreakpointResolverFileLine::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + SymbolContextList sc_list; + + assert (m_breakpoint != NULL); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + // There is a tricky bit here. You can have two compilation units that #include the same file, and + // in one of them the function at m_line_number is used (and so code and a line entry for it is generated) but in the + // other it isn't. If we considered the CU's independently, then in the second inclusion, we'd move the breakpoint + // to the next function that actually generated code in the header file. That would end up being confusing. + // So instead, we do the CU iterations by hand here, then scan through the complete list of matches, and figure out + // the closest line number match, and only set breakpoints on that match. + + // Note also that if file_spec only had a file name and not a directory, there may be many different file spec's in + // the resultant list. The closest line match for one will not be right for some totally different file. + // So we go through the match list and pull out the sets that have the same file spec in their line_entry + // and treat each set separately. + + const size_t num_comp_units = context.module_sp->GetNumCompileUnits(); + for (size_t i = 0; i < num_comp_units; i++) + { + CompUnitSP cu_sp (context.module_sp->GetCompileUnitAtIndex (i)); + if (cu_sp) + { + if (filter.CompUnitPasses(*cu_sp)) + cu_sp->ResolveSymbolContext (m_file_spec, m_line_number, m_inlines, false, eSymbolContextEverything, sc_list); + } + } + + while (sc_list.GetSize() > 0) + { + SymbolContextList tmp_sc_list; + unsigned current_idx = 0; + SymbolContext sc; + bool first_entry = true; + + FileSpec match_file_spec; + uint32_t closest_line_number = UINT32_MAX; + + // Pull out the first entry, and all the others that match its file spec, and stuff them in the tmp list. + while (current_idx < sc_list.GetSize()) + { + bool matches; + + sc_list.GetContextAtIndex (current_idx, sc); + if (first_entry) + { + match_file_spec = sc.line_entry.file; + matches = true; + first_entry = false; + } + else + matches = (sc.line_entry.file == match_file_spec); + + if (matches) + { + tmp_sc_list.Append (sc); + sc_list.RemoveContextAtIndex(current_idx); + + // ResolveSymbolContext will always return a number that is >= the line number you pass in. + // So the smaller line number is always better. + if (sc.line_entry.line < closest_line_number) + closest_line_number = sc.line_entry.line; + } + else + current_idx++; + } + + // Okay, we've found the closest line number match, now throw away all the others: + + current_idx = 0; + while (current_idx < tmp_sc_list.GetSize()) + { + if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) + { + if (sc.line_entry.line != closest_line_number) + tmp_sc_list.RemoveContextAtIndex(current_idx); + else + current_idx++; + } + } + + // Next go through and see if there are line table entries that are contiguous, and if so keep only the + // first of the contiguous range: + + lldb::addr_t last_end_addr = LLDB_INVALID_ADDRESS; + current_idx = 0; + while (current_idx < tmp_sc_list.GetSize()) + { + if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) + { + lldb::addr_t start_file_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); + lldb::addr_t end_file_addr = start_file_addr + sc.line_entry.range.GetByteSize(); + + if (start_file_addr == last_end_addr) + tmp_sc_list.RemoveContextAtIndex(current_idx); + else + current_idx++; + + last_end_addr = end_file_addr; + } + } + + // and make breakpoints out of the closest line number match. + + uint32_t tmp_sc_list_size = tmp_sc_list.GetSize(); + + for (uint32_t i = 0; i < tmp_sc_list_size; i++) + { + if (tmp_sc_list.GetContextAtIndex(i, sc)) + { + Address line_start = sc.line_entry.range.GetBaseAddress(); + if (line_start.IsValid()) + { + if (filter.AddressPasses(line_start)) + { + // If the line number is before the prologue end, move it there... + bool skipped_prologue = false; + if (m_skip_prologue) + { + if (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 (m_breakpoint->AddLocation(line_start)); + if (log && bp_loc_sp && !m_breakpoint->IsInternal()) + { + StreamString s; + bp_loc_sp->GetDescription (&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Added location (skipped prologue: %s): %s \n", skipped_prologue ? "yes" : "no", s.GetData()); + } + } + else if (log) + { + log->Printf ("Breakpoint at file address 0x%" PRIx64 " for %s:%d didn't pass the filter.\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString("<Unknown>"), + m_line_number); + } + } + else + { + if (log) + log->Printf ("error: Unable to set breakpoint at file address 0x%" PRIx64 " for %s:%d\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString("<Unknown>"), + m_line_number); + } + } + } + } + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverFileLine::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +BreakpointResolverFileLine::GetDescription (Stream *s) +{ + s->Printf ("file = '%s', line = %u", m_file_spec.GetPath().c_str(), m_line_number); +} + +void +BreakpointResolverFileLine::Dump (Stream *s) const +{ + +} + diff --git a/source/Breakpoint/BreakpointResolverFileRegex.cpp b/source/Breakpoint/BreakpointResolverFileRegex.cpp new file mode 100644 index 000000000000..de974d04894a --- /dev/null +++ b/source/Breakpoint/BreakpointResolverFileRegex.cpp @@ -0,0 +1,134 @@ +//===-- BreakpointResolverFileRegex.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverFileRegex.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverFileRegex: +//---------------------------------------------------------------------- +BreakpointResolverFileRegex::BreakpointResolverFileRegex +( + Breakpoint *bkpt, + RegularExpression ®ex +) : + BreakpointResolver (bkpt, BreakpointResolver::FileLineResolver), + m_regex (regex) +{ +} + +BreakpointResolverFileRegex::~BreakpointResolverFileRegex () +{ +} + +Searcher::CallbackReturn +BreakpointResolverFileRegex::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + + assert (m_breakpoint != NULL); + if (!context.target_sp) + return eCallbackReturnContinue; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + CompileUnit *cu = context.comp_unit; + FileSpec cu_file_spec = *(static_cast<FileSpec *>(cu)); + 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++) + { + uint32_t start_idx = 0; + bool exact = false; + while (1) + { + LineEntry line_entry; + + // Cycle through all the line entries that might match this one: + start_idx = cu->FindLineEntry (start_idx, line_matches[i], NULL, exact, &line_entry); + if (start_idx == UINT32_MAX) + break; + exact = true; + start_idx++; + + Address line_start = line_entry.range.GetBaseAddress(); + if (line_start.IsValid()) + { + if (filter.AddressPasses(line_start)) + { + BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(line_start)); + if (log && bp_loc_sp && !m_breakpoint->IsInternal()) + { + StreamString s; + bp_loc_sp->GetDescription (&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Added location: %s\n", s.GetData()); + } + } + else if (log) + { + log->Printf ("Breakpoint at file address 0x%" PRIx64 " for %s:%d didn't pass filter.\n", + line_start.GetFileAddress(), + cu_file_spec.GetFilename().AsCString("<Unknown>"), + line_matches[i]); + } + } + else + { + if (log) + log->Printf ("error: Unable to set breakpoint at file address 0x%" PRIx64 " for %s:%d\n", + line_start.GetFileAddress(), + cu_file_spec.GetFilename().AsCString("<Unknown>"), + line_matches[i]); + } + + } + } + assert (m_breakpoint != NULL); + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverFileRegex::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +BreakpointResolverFileRegex::GetDescription (Stream *s) +{ + s->Printf ("source regex = \"%s\"", m_regex.GetText()); +} + +void +BreakpointResolverFileRegex::Dump (Stream *s) const +{ + +} + diff --git a/source/Breakpoint/BreakpointResolverName.cpp b/source/Breakpoint/BreakpointResolverName.cpp new file mode 100644 index 000000000000..27f85653d648 --- /dev/null +++ b/source/Breakpoint/BreakpointResolverName.cpp @@ -0,0 +1,357 @@ +//===-- BreakpointResolverName.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverName.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt, + const char *name_cstr, + uint32_t name_type_mask, + Breakpoint::MatchType type, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (), + m_regex (), + m_match_type (type), + m_skip_prologue (skip_prologue) +{ + + if (m_match_type == Breakpoint::Regexp) + { + if (!m_regex.Compile (name_cstr)) + { + 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, + uint32_t name_type_mask, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_match_type (Breakpoint::Exact), + 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, + uint32_t name_type_mask, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_match_type (Breakpoint::Exact), + 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, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (NULL), + m_regex (func_regex), + m_match_type (Breakpoint::Regexp), + m_skip_prologue (skip_prologue) +{ +} + +BreakpointResolverName::BreakpointResolverName +( + Breakpoint *bkpt, + const char *class_name, + const char *method, + Breakpoint::MatchType type, + bool skip_prologue +) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (class_name), + m_regex (), + m_match_type (type), + m_skip_prologue (skip_prologue) +{ + LookupInfo lookup; + lookup.name.SetCString(method); + lookup.lookup_name = lookup.name; + lookup.name_type_mask = eFunctionNameTypeMethod; + lookup.match_name_after_lookup = false; + m_lookups.push_back (lookup); +} + +BreakpointResolverName::~BreakpointResolverName () +{ +} + +void +BreakpointResolverName::AddNameLookup (const ConstString &name, uint32_t name_type_mask) +{ + ObjCLanguageRuntime::MethodName objc_method(name.GetCString(), false); + if (objc_method.IsValid(false)) + { + std::vector<ConstString> objc_names; + objc_method.GetFullNames(objc_names, true); + for (ConstString objc_name : objc_names) + { + LookupInfo lookup; + lookup.name = name; + lookup.lookup_name = objc_name; + lookup.name_type_mask = eFunctionNameTypeFull; + lookup.match_name_after_lookup = false; + m_lookups.push_back (lookup); + } + } + else + { + LookupInfo lookup; + lookup.name = name; + Module::PrepareForFunctionNameLookup(lookup.name, name_type_mask, lookup.lookup_name, lookup.name_type_mask, lookup.match_name_after_lookup); + m_lookups.push_back (lookup); + } +} + + +void +BreakpointResolverName::LookupInfo::Prune (SymbolContextList &sc_list, size_t start_idx) const +{ + if (match_name_after_lookup && name) + { + SymbolContext sc; + size_t i = start_idx; + while (i < sc_list.GetSize()) + { + if (!sc_list.GetContextAtIndex(i, sc)) + break; + ConstString full_name (sc.GetFunctionName()); + if (full_name && ::strstr(full_name.GetCString(), name.GetCString()) == NULL) + { + sc_list.RemoveContextAtIndex(i); + } + else + { + ++i; + } + } + } +} + + +// 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, + bool containing +) +{ + SymbolContextList func_list; + //SymbolContextList sym_list; + + uint32_t i; + bool new_location; + Address break_addr; + assert (m_breakpoint != NULL); + + 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; + const bool include_symbols = filter_by_cu == false; + const bool include_inlines = true; + const bool append = true; + + switch (m_match_type) + { + case Breakpoint::Exact: + if (context.module_sp) + { + for (const LookupInfo &lookup : m_lookups) + { + const size_t start_func_idx = func_list.GetSize(); + context.module_sp->FindFunctions (lookup.lookup_name, + NULL, + lookup.name_type_mask, + include_symbols, + include_inlines, + append, + 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 filterning by CU + include_inlines, + append, + 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) + { + uint32_t num_functions = func_list.GetSize(); + + for (size_t idx = 0; idx < num_functions; idx++) + { + SymbolContext sc; + func_list.GetContextAtIndex(idx, sc); + if (!sc.comp_unit || !filter.CompUnitPasses(*sc.comp_unit)) + { + func_list.RemoveContextAtIndex(idx); + num_functions--; + idx--; + } + } + } + + // Remove any duplicates between the funcion 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)) + { + 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) + { + 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); + } + } + + if (break_addr.IsValid()) + { + if (filter.AddressPasses(break_addr)) + { + BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(break_addr, &new_location)); + if (bp_loc_sp && new_location && !m_breakpoint->IsInternal()) + { + if (log) + { + StreamString s; + bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Added location: %s\n", s.GetData()); + } + } + } + } + } + } + } + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverName::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +BreakpointResolverName::GetDescription (Stream *s) +{ + if (m_match_type == Breakpoint::Regexp) + s->Printf("regex = '%s'", m_regex.GetText()); + else + { + size_t num_names = m_lookups.size(); + if (num_names == 1) + s->Printf("name = '%s'", m_lookups[0].name.GetCString()); + else + { + s->Printf("names = {"); + for (size_t i = 0; i < num_names - 1; i++) + { + s->Printf ("'%s', ", m_lookups[i].name.GetCString()); + } + s->Printf ("'%s'}", m_lookups[num_names - 1].name.GetCString()); + } + } +} + +void +BreakpointResolverName::Dump (Stream *s) const +{ + +} + diff --git a/source/Breakpoint/BreakpointSite.cpp b/source/Breakpoint/BreakpointSite.cpp new file mode 100644 index 000000000000..fa5d8c1f9f81 --- /dev/null +++ b/source/Breakpoint/BreakpointSite.cpp @@ -0,0 +1,234 @@ +//===-- BreakpointSite.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointSite.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSiteList.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.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(); + return m_owners.ShouldStop (context); +} + +bool +BreakpointSite::IsBreakpointAtThisSite (lldb::break_id_t bp_id) +{ + 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 == NULL) + 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) +{ + 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) +{ + m_owners.Add(owner); +} + +size_t +BreakpointSite::RemoveOwner (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) +{ + m_owners.Remove(break_id, break_loc_id); + return m_owners.GetSize(); +} + +size_t +BreakpointSite::GetNumberOfOwners () +{ + return m_owners.GetSize(); +} + +BreakpointLocationSP +BreakpointSite::GetOwnerAtIndex (size_t index) +{ + return m_owners.GetByIndex (index); +} + +bool +BreakpointSite::ValidForThisThread (Thread *thread) +{ + return m_owners.ValidForThisThread(thread); +} + +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; +} diff --git a/source/Breakpoint/BreakpointSiteList.cpp b/source/Breakpoint/BreakpointSiteList.cpp new file mode 100644 index 000000000000..68c4af18ec5e --- /dev/null +++ b/source/Breakpoint/BreakpointSiteList.cpp @@ -0,0 +1,240 @@ +//===-- BreakpointSiteList.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointSiteList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include <algorithm> + +using namespace lldb; +using namespace lldb_private; + +BreakpointSiteList::BreakpointSiteList() : + m_mutex (Mutex::eMutexTypeRecursive), + 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(); + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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.get()->GetID(); + } + +private: + const lldb::break_id_t m_break_id; +}; + +BreakpointSiteList::collection::iterator +BreakpointSiteList::GetIDIterator (lldb::break_id_t break_id) +{ + Mutex::Locker locker(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 +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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 +{ + Mutex::Locker locker(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; + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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: ", 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.get()->Dump(s); + s->IndentLess(); +} + +void +BreakpointSiteList::ForEach (std::function <void(BreakpointSite *)> const &callback) +{ + Mutex::Locker locker(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; + + Mutex::Locker locker(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/source/Breakpoint/Stoppoint.cpp b/source/Breakpoint/Stoppoint.cpp new file mode 100644 index 000000000000..583ab47005f5 --- /dev/null +++ b/source/Breakpoint/Stoppoint.cpp @@ -0,0 +1,46 @@ +//===-- Stoppoint.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/Stoppoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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/source/Breakpoint/StoppointCallbackContext.cpp b/source/Breakpoint/StoppointCallbackContext.cpp new file mode 100644 index 000000000000..2266c3e429c6 --- /dev/null +++ b/source/Breakpoint/StoppointCallbackContext.cpp @@ -0,0 +1,39 @@ +//===-- StoppointCallbackContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointCallbackContext.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +StoppointCallbackContext::StoppointCallbackContext() : + event (NULL), + 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 = NULL; + exe_ctx_ref.Clear(); + is_synchronous = false; +} diff --git a/source/Breakpoint/StoppointLocation.cpp b/source/Breakpoint/StoppointLocation.cpp new file mode 100644 index 000000000000..092caa5a9322 --- /dev/null +++ b/source/Breakpoint/StoppointLocation.cpp @@ -0,0 +1,48 @@ +//===-- StoppointLocation.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointLocation.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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_hw_preferred(hardware), + m_hw_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_hw_preferred(hardware), + m_hw_index(LLDB_INVALID_INDEX32), + m_byte_size(byte_size), + m_hit_count(0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StoppointLocation::~StoppointLocation() +{ +} diff --git a/source/Breakpoint/Watchpoint.cpp b/source/Breakpoint/Watchpoint.cpp new file mode 100644 index 000000000000..45559b1901ad --- /dev/null +++ b/source/Breakpoint/Watchpoint.cpp @@ -0,0 +1,489 @@ +//===-- Watchpoint.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/Watchpoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.h" + +using namespace lldb; +using namespace lldb_private; + +Watchpoint::Watchpoint (Target& target, lldb::addr_t addr, uint32_t size, const ClangASTType *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. + ClangASTContext *ast_context = target.GetScratchClangASTContext(); + m_type = ast_context->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() +{ +} + +// 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, BatonSP (new Baton(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; + return; +} + +std::string +Watchpoint::GetWatchSpec() +{ + return m_watch_spec_str; +} + +void +Watchpoint::SetWatchSpec (const std::string &str) +{ + m_watch_spec_str = str; + return; +} + +// 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.AsCString(), watch_address, m_type); + m_new_value_sp = m_new_value_sp->CreateConstantValue(watch_name); + if (m_new_value_sp && m_new_value_sp->GetError().Success()) + return true; + else + return false; +} + +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(); + + if (!IsEnabled()) + return false; + + if (GetHitCount() <= GetIgnoreCount()) + return false; + + return true; +} + +void +Watchpoint::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + DumpWithLevel(s, level); + return; +} + +void +Watchpoint::Dump(Stream *s) const +{ + DumpWithLevel(s, lldb::eDescriptionLevelBrief); +} + +// If prefix is NULL, 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) + { + s->Printf("\n%sold value: %s", prefix, m_old_value_sp->GetValueAsCString()); + } + if (m_new_value_sp) + { + s->Printf("\n%snew value: %s", prefix, m_new_value_sp->GetValueAsCString()); + } +} + +void +Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) const +{ + if (s == NULL) + 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; +} + +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 == NULL || condition[0] == '\0') + { + if (m_condition_ap.get()) + m_condition_ap.reset(); + } + else + { + // Pass NULL for expr_prefix (no translation-unit level definitions). + m_condition_ap.reset(new ClangUserExpression (condition, NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny)); + } + SendWatchpointChangedEvent (eWatchpointEventTypeConditionChanged); +} + +const char * +Watchpoint::GetConditionText () const +{ + if (m_condition_ap.get()) + return m_condition_ap->GetUserText(); + else + return NULL; +} + +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 == NULL) + 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 () +{ +} + +const ConstString & +Watchpoint::WatchpointEventData::GetFlavorString () +{ + static ConstString g_flavor ("Watchpoint::WatchpointEventData"); + return g_flavor; +} + +const 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 NULL; +} + +WatchpointEventType +Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent (const EventSP &event_sp) +{ + const WatchpointEventData *data = GetEventDataFromEvent (event_sp.get()); + + if (data == NULL) + 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/source/Breakpoint/WatchpointList.cpp b/source/Breakpoint/WatchpointList.cpp new file mode 100644 index 000000000000..6d62dffd22cc --- /dev/null +++ b/source/Breakpoint/WatchpointList.cpp @@ -0,0 +1,306 @@ +//===-- WatchpointList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Breakpoint/Watchpoint.h" + +using namespace lldb; +using namespace lldb_private; + +WatchpointList::WatchpointList() : + m_watchpoints (), + m_mutex (Mutex::eMutexTypeRecursive), + 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) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (m_mutex); + s->Printf("%p: ", 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; + Mutex::Locker locker (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)->GetLoadAddress() == addr) { + wp_sp = *pos; + break; + } + } + + return wp_sp; +} + +const WatchpointSP +WatchpointList::FindBySpec (std::string spec) const +{ + WatchpointSP wp_sp; + Mutex::Locker locker (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; + Mutex::Locker locker (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) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker (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; + Mutex::Locker locker (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) +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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 (Mutex::Locker &locker) +{ + return locker.Lock (m_mutex); +} diff --git a/source/Breakpoint/WatchpointOptions.cpp b/source/Breakpoint/WatchpointOptions.cpp new file mode 100644 index 000000000000..c2c9696c4ce7 --- /dev/null +++ b/source/Breakpoint/WatchpointOptions.cpp @@ -0,0 +1,241 @@ +//===-- WatchpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/WatchpointOptions.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Value.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.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_ap () +{ +} + +//---------------------------------------------------------------------- +// 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_ap () +{ + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get())); +} + +//---------------------------------------------------------------------- +// 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_ap.get() != NULL) + m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); + 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() +{ +} + +//------------------------------------------------------------------ +// 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->m_data : NULL, + context, + watch_id); + } + else + return true; +} + +bool +WatchpointOptions::HasCallback () +{ + return m_callback != WatchpointOptions::NullCallback; +} + +const ThreadSpec * +WatchpointOptions::GetThreadSpecNoCreate () const +{ + return m_thread_spec_ap.get(); +} + +ThreadSpec * +WatchpointOptions::GetThreadSpec () +{ + if (m_thread_spec_ap.get() == NULL) + m_thread_spec_ap.reset (new ThreadSpec()); + + return m_thread_spec_ap.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, level); + } +} +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() != NULL && 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_ap.get()) + m_thread_spec_ap->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 (Stream *s, lldb::DescriptionLevel level) const +{ + CommandData *data = (CommandData *)m_data; + + if (level == eDescriptionLevelBrief) + { + s->Printf (", commands = %s", (data && data->user_source.GetSize() > 0) ? "yes" : "no"); + return; + } + + s->IndentMore (); + s->Indent("watchpoint commands:\n"); + + s->IndentMore (); + if (data && data->user_source.GetSize() > 0) + { + const size_t num_strings = data->user_source.GetSize(); + for (size_t i = 0; i < num_strings; ++i) + { + s->Indent(data->user_source.GetStringAtIndex(i)); + s->EOL(); + } + } + else + { + s->PutCString ("No commands.\n"); + } + s->IndentLess (); + s->IndentLess (); +} + |
