Rework how resetting breakpoints in changed modules works. Try to match up old

locations with new ones if possible.

Next up some test cases...

llvm-svn: 217551
This commit is contained in:
Jim Ingham 2014-09-10 21:40:47 +00:00
parent 5418f40127
commit 77fd738f58
9 changed files with 396 additions and 50 deletions

View File

@ -203,13 +203,28 @@ public:
/// Tell this breakpoint to scan a given module list and resolve any
/// new locations that match the breakpoint's specifications.
///
/// @param[in] changed_modules
/// @param[in] module_list
/// The list of modules to look in for new locations.
///
/// @param[in] send_event
/// If \b true, send a breakpoint location added event for non-internal breakpoints.
//------------------------------------------------------------------
void
ResolveBreakpointInModules (ModuleList &changed_modules);
ResolveBreakpointInModules (ModuleList &module_list, bool send_event = true);
//------------------------------------------------------------------
/// Tell this breakpoint to scan a given module list and resolve any
/// new locations that match the breakpoint's specifications.
///
/// @param[in] changed_modules
/// The list of modules to look in for new locations.
///
/// @param[in] new_locations
/// Fills new_locations with the new locations that were made.
//------------------------------------------------------------------
void
ResolveBreakpointInModules (ModuleList &module_list, BreakpointLocationCollection &new_locations);
//------------------------------------------------------------------
/// Like ResolveBreakpointInModules, but allows for "unload" events, in
/// which case we will remove any locations that are in modules that got

View File

@ -52,6 +52,7 @@ class BreakpointLocation :
public StoppointLocation
{
public:
friend class BreakpointLocationList;
~BreakpointLocation ();
@ -374,6 +375,19 @@ public:
m_is_reexported = is_reexported;
}
//------------------------------------------------------------------
/// Returns whether the two breakpoint locations might represent "equivalent locations".
/// This is used when modules changed to determine if a Location in the old module might
/// be the "same as" the input location.
///
/// @param[in] location
/// The location to compare against.
///
/// @return
/// \b true or \b false as given in the description above.
//------------------------------------------------------------------
bool EquivalentToLocation(BreakpointLocation &location);
protected:
friend class BreakpointLocationList;
friend class Process;
@ -396,8 +410,11 @@ protected:
bool
IgnoreCountShouldStop();
private:
void
SwapLocation (lldb::BreakpointLocationSP swap_from);
//------------------------------------------------------------------
// Constructors and Destructors

View File

@ -16,6 +16,7 @@
// Other libraries and framework includes
// Project includes
#include "lldb/lldb-private.h"
#include "lldb/Utility/Iterable.h"
namespace lldb_private {
@ -202,6 +203,14 @@ private:
collection m_break_loc_collection;
public:
typedef AdaptedIterable<collection, lldb::BreakpointLocationSP, vector_adapter> BreakpointLocationCollectionIterable;
BreakpointLocationCollectionIterable
BreakpointLocations()
{
return BreakpointLocationCollectionIterable(m_break_loc_collection);
}
};
} // namespace lldb_private

View File

@ -19,6 +19,7 @@
#include "lldb/lldb-private.h"
#include "lldb/Core/Address.h"
#include "lldb/Host/Mutex.h"
#include "lldb/Utility/Iterable.h"
namespace lldb_private {
@ -248,12 +249,18 @@ protected:
AddLocation (const Address &addr,
bool resolve_indirect_symbols,
bool *new_location = NULL);
void
SwapLocation (lldb::BreakpointLocationSP to_location_sp, lldb::BreakpointLocationSP from_location_sp);
bool
RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp);
void
RemoveInvalidLocations (const ArchSpec &arch);
void
Compact();
typedef std::vector<lldb::BreakpointLocationSP> collection;
typedef std::map<lldb_private::Address,
@ -266,6 +273,14 @@ protected:
mutable Mutex m_mutex;
lldb::break_id_t m_next_id;
BreakpointLocationCollection *m_new_location_recorder;
public:
typedef AdaptedIterable<collection, lldb::BreakpointLocationSP, vector_adapter> BreakpointLocationIterable;
BreakpointLocationIterable
BreakpointLocations()
{
return BreakpointLocationIterable(m_locations);
}
};
} // namespace lldb_private

View File

@ -586,6 +586,13 @@ public:
return ModuleIterable(m_modules, GetMutex());
}
typedef AdaptedIterable<collection, lldb::ModuleSP, vector_adapter> ModuleIterableNoLocking;
ModuleIterableNoLocking
ModulesNoLocking ()
{
return ModuleIterableNoLocking(m_modules);
}
};
} // namespace lldb_private

View File

@ -613,6 +613,7 @@
4C3ADCD61810D88B00357218 /* BreakpointResolverFileRegex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CAA56141422D986001FFA01 /* BreakpointResolverFileRegex.cpp */; };
4C6649A014EEE7F100B0316F /* StreamCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C66499F14EEE7F100B0316F /* StreamCallback.h */; };
4C6649A314EEE81000B0316F /* StreamCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C6649A214EEE81000B0316F /* StreamCallback.cpp */; };
4C73152219B7D71700F865A4 /* Iterable.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C73152119B7D71700F865A4 /* Iterable.h */; };
4CABA9E0134A8BCD00539BDD /* ValueObjectMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CABA9DF134A8BCD00539BDD /* ValueObjectMemory.cpp */; };
4CCA644D13B40B82003BDF98 /* ItaniumABILanguageRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA643D13B40B82003BDF98 /* ItaniumABILanguageRuntime.cpp */; };
4CCA645013B40B82003BDF98 /* AppleObjCRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CCA644213B40B82003BDF98 /* AppleObjCRuntime.cpp */; };
@ -1892,6 +1893,7 @@
4C626533130F1B0A00C889F6 /* StreamTee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamTee.h; path = include/lldb/Core/StreamTee.h; sourceTree = "<group>"; };
4C66499F14EEE7F100B0316F /* StreamCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StreamCallback.h; path = include/lldb/Core/StreamCallback.h; sourceTree = "<group>"; };
4C6649A214EEE81000B0316F /* StreamCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamCallback.cpp; path = source/Core/StreamCallback.cpp; sourceTree = "<group>"; };
4C73152119B7D71700F865A4 /* Iterable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Iterable.h; path = include/lldb/Utility/Iterable.h; sourceTree = "<group>"; };
4C7CF7E31295E10E00B4FBB5 /* ThreadPlanCallUserExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ThreadPlanCallUserExpression.h; path = include/lldb/Target/ThreadPlanCallUserExpression.h; sourceTree = "<group>"; };
4C7CF7E51295E12B00B4FBB5 /* ThreadPlanCallUserExpression.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanCallUserExpression.cpp; path = source/Target/ThreadPlanCallUserExpression.cpp; sourceTree = "<group>"; };
4C98D3DA118FB96F00E575D0 /* ClangFunction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClangFunction.cpp; path = source/Expression/ClangFunction.cpp; sourceTree = "<group>"; };
@ -2952,6 +2954,7 @@
264A12FE137252C700875C42 /* ARM64_DWARF_Registers.cpp */,
26F996A8119B79C300412154 /* ARM_GCC_Registers.h */,
264723A511FA076E00DE380C /* CleanUp.h */,
4C73152119B7D71700F865A4 /* Iterable.h */,
26D1804416CEE12500EDFB5B /* KQueue.h */,
26D1803C16CEBFD300EDFB5B /* KQueue.cpp */,
94031A9F13CF5B3D00DCFF3C /* PriorityPointerPair.h */,
@ -4483,6 +4486,7 @@
2697A39515E404BA003E682C /* OptionValueArch.h in Headers */,
26474CBF18D0CB2D0073DEBA /* RegisterContextMach_i386.h in Headers */,
26474CC118D0CB2D0073DEBA /* RegisterContextMach_x86_64.h in Headers */,
4C73152219B7D71700F865A4 /* Iterable.h in Headers */,
2698699D15E6CBD0002415FF /* OperatingSystemPython.h in Headers */,
232CB618191E00CD00EF39FC /* NativeBreakpointList.h in Headers */,
260D9B2715EC369500960137 /* ModuleSpec.h in Headers */,

View File

@ -20,11 +20,14 @@
#include "lldb/Breakpoint/BreakpointResolver.h"
#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.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/CompileUnit.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadSpec.h"
@ -349,10 +352,41 @@ Breakpoint::ResolveBreakpoint ()
}
void
Breakpoint::ResolveBreakpointInModules (ModuleList &module_list)
Breakpoint::ResolveBreakpointInModules (ModuleList &module_list, BreakpointLocationCollection &new_locations)
{
m_locations.StartRecordingNewLocations(new_locations);
m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
m_locations.StopRecordingNewLocations();
}
void
Breakpoint::ResolveBreakpointInModules (ModuleList &module_list, bool send_event)
{
if (m_resolver_sp)
m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
{
// If this is not an internal breakpoint, set up to record the new locations, then dispatch
// an event with the new locations.
if (!IsInternal() && send_event)
{
BreakpointEventData *new_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded,
shared_from_this());
ResolveBreakpointInModules (module_list, new_locations_event->GetBreakpointLocationCollection());
if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0)
{
SendBreakpointChangedEvent (new_locations_event);
}
else
delete new_locations_event;
}
else
{
m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
}
}
}
void
@ -368,6 +402,11 @@ Breakpoint::ClearAllBreakpointSites ()
void
Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_locations)
{
Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
if (log)
log->Printf ("Breakpoint::ModulesChanged: num_modules: %zu load: %i delete_locations: %i\n",
module_list.GetSize(), load, delete_locations);
Mutex::Locker modules_mutex(module_list.GetMutex());
if (load)
{
@ -383,32 +422,27 @@ Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_loca
// 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++)
for (ModuleSP module_sp : module_list.ModulesNoLocking())
{
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++)
for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations())
{
BreakpointLocationSP break_loc = m_locations.GetByIndex(loc_idx);
if (!break_loc->IsEnabled())
if (!break_loc_sp->IsEnabled())
continue;
SectionSP section_sp (break_loc->GetAddress().GetSection());
SectionSP section_sp (break_loc_sp->GetAddress().GetSection());
if (!section_sp || section_sp->GetModule() == module_sp)
{
if (!seen)
seen = true;
if (!break_loc->ResolveBreakpointSite())
if (!break_loc_sp->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());
break_loc_sp->GetID(), GetID());
}
}
}
@ -420,28 +454,7 @@ Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_loca
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);
ResolveBreakpointInModules(new_modules);
}
}
else
@ -498,21 +511,251 @@ Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_loca
}
}
namespace
{
static bool
SymbolContextsMightBeEquivalent(SymbolContext &old_sc, SymbolContext &new_sc)
{
bool equivalent_scs = false;
if (old_sc.module_sp.get() == new_sc.module_sp.get())
{
// If these come from the same module, we can directly compare the pointers:
if (old_sc.comp_unit && new_sc.comp_unit
&& (old_sc.comp_unit == new_sc.comp_unit))
{
if (old_sc.function && new_sc.function
&& (old_sc.function == new_sc.function))
{
equivalent_scs = true;
}
}
else if (old_sc.symbol && new_sc.symbol
&& (old_sc.symbol == new_sc.symbol))
{
equivalent_scs = true;
}
}
else
{
// Otherwise we will compare by name...
if (old_sc.comp_unit && new_sc.comp_unit)
{
if (FileSpec::Equal(*old_sc.comp_unit, *new_sc.comp_unit, true))
{
// Now check the functions:
if (old_sc.function && new_sc.function
&& (old_sc.function->GetName() == new_sc.function->GetName()))
{
equivalent_scs = true;
}
}
}
else if (old_sc.symbol && new_sc.symbol)
{
if (Mangled::Compare(old_sc.symbol->GetMangled(), new_sc.symbol->GetMangled()) == 0)
{
equivalent_scs = true;
}
}
}
return equivalent_scs;
}
}
void
Breakpoint::ModuleReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp)
{
ModuleList temp_list;
temp_list.Append (new_module_sp);
ModulesChanged (temp_list, true);
Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
if (log)
log->Printf ("Breakpoint::ModulesReplaced for %s\n",
old_module_sp->GetSpecificationDescription().c_str());
// First find all the locations that are in the old module
BreakpointLocationCollection old_break_locs;
for (BreakpointLocationSP break_loc_sp : m_locations.BreakpointLocations())
{
SectionSP section_sp = break_loc_sp->GetAddress().GetSection();
if (section_sp && section_sp->GetModule() == old_module_sp)
{
old_break_locs.Add(break_loc_sp);
}
}
size_t num_old_locations = old_break_locs.GetSize();
if (num_old_locations == 0)
{
// There were no locations in the old module, so we just need to check if there were any in the new module.
ModuleList temp_list;
temp_list.Append (new_module_sp);
ResolveBreakpointInModules(temp_list);
}
else
{
// First search the new module for locations.
// Then compare this with the old list, copy over locations that "look the same"
// Then delete the old locations.
// Finally remember to post the creation event.
//
// Two locations are the same if they have the same comp unit & function (by name) and there are the same number
// of locations in the old function as in the new one.
ModuleList temp_list;
temp_list.Append (new_module_sp);
BreakpointLocationCollection new_break_locs;
ResolveBreakpointInModules(temp_list, new_break_locs);
BreakpointLocationCollection locations_to_remove;
BreakpointLocationCollection locations_to_announce;
size_t num_new_locations = new_break_locs.GetSize();
if (num_new_locations > 0)
{
// Break out the case of one location -> one location since that's the most common one, and there's no need
// to build up the structures needed for the merge in that case.
if (num_new_locations == 1 && num_old_locations == 1)
{
bool equivalent_locations = false;
SymbolContext old_sc, new_sc;
// The only way the old and new location can be equivalent is if they have the same amount of information:
BreakpointLocationSP old_loc_sp = old_break_locs.GetByIndex(0);
BreakpointLocationSP new_loc_sp = new_break_locs.GetByIndex(0);
if (old_loc_sp->GetAddress().CalculateSymbolContext(&old_sc)
== new_loc_sp->GetAddress().CalculateSymbolContext(&new_sc))
{
equivalent_locations = SymbolContextsMightBeEquivalent(old_sc, new_sc);
}
if (equivalent_locations)
{
m_locations.SwapLocation (old_loc_sp, new_loc_sp);
}
else
{
locations_to_remove.Add(old_loc_sp);
locations_to_announce.Add(new_loc_sp);
}
}
else
{
//We don't want to have to keep computing the SymbolContexts for these addresses over and over,
// so lets get them up front:
typedef std::map<lldb::break_id_t, SymbolContext> IDToSCMap;
IDToSCMap old_sc_map;
for (size_t idx = 0; idx < num_old_locations; idx++)
{
SymbolContext sc;
BreakpointLocationSP bp_loc_sp = old_break_locs.GetByIndex(idx);
lldb::break_id_t loc_id = bp_loc_sp->GetID();
bp_loc_sp->GetAddress().CalculateSymbolContext(&old_sc_map[loc_id]);
}
std::map<lldb::break_id_t, SymbolContext> new_sc_map;
for (size_t idx = 0; idx < num_new_locations; idx++)
{
SymbolContext sc;
BreakpointLocationSP bp_loc_sp = new_break_locs.GetByIndex(idx);
lldb::break_id_t loc_id = bp_loc_sp->GetID();
bp_loc_sp->GetAddress().CalculateSymbolContext(&new_sc_map[loc_id]);
}
// Take an element from the old Symbol Contexts
while (old_sc_map.size() > 0)
{
lldb::break_id_t old_id = old_sc_map.begin()->first;
SymbolContext &old_sc = old_sc_map.begin()->second;
// Count the number of entries equivalent to this SC for the old list:
std::vector<lldb::break_id_t> old_id_vec;
old_id_vec.push_back(old_id);
IDToSCMap::iterator tmp_iter;
for (tmp_iter = ++old_sc_map.begin(); tmp_iter != old_sc_map.end(); tmp_iter++)
{
if (SymbolContextsMightBeEquivalent (old_sc, tmp_iter->second))
old_id_vec.push_back (tmp_iter->first);
}
// Now find all the equivalent locations in the new list.
std::vector<lldb::break_id_t> new_id_vec;
for (tmp_iter = new_sc_map.begin(); tmp_iter != new_sc_map.end(); tmp_iter++)
{
if (SymbolContextsMightBeEquivalent (old_sc, tmp_iter->second))
new_id_vec.push_back(tmp_iter->first);
}
// Alright, if we have the same number of potentially equivalent locations in the old
// and new modules, we'll just map them one to one in ascending ID order (assuming the
// resolver's order would match the equivalent ones.
// Otherwise, we'll dump all the old ones, and just take the new ones, erasing the elements
// from both maps as we go.
if (old_id_vec.size() == new_id_vec.size())
{
sort(old_id_vec.begin(), old_id_vec.end());
sort(new_id_vec.begin(), new_id_vec.end());
size_t num_elements = old_id_vec.size();
for (size_t idx = 0; idx < num_elements; idx++)
{
BreakpointLocationSP old_loc_sp = old_break_locs.FindByIDPair(GetID(), old_id_vec[idx]);
BreakpointLocationSP new_loc_sp = new_break_locs.FindByIDPair(GetID(), new_id_vec[idx]);
m_locations.SwapLocation(old_loc_sp, new_loc_sp);
old_sc_map.erase(old_id_vec[idx]);
new_sc_map.erase(new_id_vec[idx]);
}
}
else
{
for (lldb::break_id_t old_id : old_id_vec)
{
locations_to_remove.Add(old_break_locs.FindByIDPair(GetID(), old_id));
old_sc_map.erase(old_id);
}
for (lldb::break_id_t new_id : new_id_vec)
{
locations_to_announce.Add(new_break_locs.FindByIDPair(GetID(), new_id));
new_sc_map.erase(new_id);
}
}
}
}
}
// Now remove the remaining old locations, and cons up a removed locations event.
// Note, we don't put the new locations that were swapped with an old location on the locations_to_remove
// list, so we don't need to worry about telling the world about removing a location we didn't tell them
// about adding.
BreakpointEventData *locations_event;
if (!IsInternal())
locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsRemoved,
shared_from_this());
else
locations_event = NULL;
// 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.
for (BreakpointLocationSP loc_sp : locations_to_remove.BreakpointLocations())
{
m_locations.RemoveLocation(loc_sp);
if (locations_event)
locations_event->GetBreakpointLocationCollection().Add(loc_sp);
}
SendBreakpointChangedEvent (locations_event);
temp_list.Clear();
temp_list.Append (old_module_sp);
ModulesChanged (temp_list, false, true);
// And announce the new ones.
if (!IsInternal())
{
locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded,
shared_from_this());
for (BreakpointLocationSP loc_sp : locations_to_announce.BreakpointLocations())
locations_event->GetBreakpointLocationCollection().Add(loc_sp);
SendBreakpointChangedEvent (locations_event);
}
m_locations.Compact();
}
}
void

View File

@ -717,4 +717,13 @@ BreakpointLocation::SendBreakpointLocationChangedEvent (lldb::BreakpointEventTyp
m_owner.GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, data);
}
}
void
BreakpointLocation::SwapLocation (BreakpointLocationSP swap_from)
{
m_address = swap_from->m_address;
m_should_resolve_indirect_functions = swap_from->m_should_resolve_indirect_functions;
m_is_reexported = swap_from->m_is_reexported;
m_is_indirect = swap_from->m_is_indirect;
m_user_expression_sp.reset();
}

View File

@ -272,6 +272,20 @@ BreakpointLocationList::AddLocation (const Address &addr, bool resolve_indirect_
return bp_loc_sp;
}
void
BreakpointLocationList::SwapLocation (BreakpointLocationSP to_location_sp, BreakpointLocationSP from_location_sp)
{
if (!from_location_sp || !to_location_sp)
return;
m_address_to_location.erase(to_location_sp->GetAddress());
to_location_sp->SwapLocation(from_location_sp);
RemoveLocation(from_location_sp);
m_address_to_location[to_location_sp->GetAddress()] = to_location_sp;
to_location_sp->ResolveBreakpointSite();
}
bool
BreakpointLocationList::RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp)
{
@ -345,3 +359,16 @@ BreakpointLocationList::StopRecordingNewLocations ()
m_new_location_recorder = NULL;
}
void
BreakpointLocationList::Compact()
{
lldb::break_id_t highest_id = 0;
for (BreakpointLocationSP loc_sp : m_locations)
{
lldb::break_id_t cur_id = loc_sp->GetID();
if (cur_id > highest_id)
highest_id = cur_id;
}
m_next_id = highest_id;
}