Breakpoint.cpp revision 263367
1//===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10
11// C Includes
12// C++ Includes
13// Other libraries and framework includes
14// Project includes
15
16#include "lldb/Core/Address.h"
17#include "lldb/Breakpoint/Breakpoint.h"
18#include "lldb/Breakpoint/BreakpointLocation.h"
19#include "lldb/Breakpoint/BreakpointLocationCollection.h"
20#include "lldb/Breakpoint/BreakpointResolver.h"
21#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
22#include "lldb/Core/Log.h"
23#include "lldb/Core/ModuleList.h"
24#include "lldb/Core/SearchFilter.h"
25#include "lldb/Core/Section.h"
26#include "lldb/Core/Stream.h"
27#include "lldb/Core/StreamString.h"
28#include "lldb/Symbol/SymbolContext.h"
29#include "lldb/Target/Target.h"
30#include "lldb/Target/ThreadSpec.h"
31#include "lldb/lldb-private-log.h"
32#include "llvm/Support/Casting.h"
33
34using namespace lldb;
35using namespace lldb_private;
36using namespace llvm;
37
38const ConstString &
39Breakpoint::GetEventIdentifier ()
40{
41    static ConstString g_identifier("event-identifier.breakpoint.changed");
42    return g_identifier;
43}
44
45//----------------------------------------------------------------------
46// Breakpoint constructor
47//----------------------------------------------------------------------
48Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool hardware) :
49    m_being_created(true),
50    m_hardware(hardware),
51    m_target (target),
52    m_filter_sp (filter_sp),
53    m_resolver_sp (resolver_sp),
54    m_options (),
55    m_locations (*this)
56{
57    m_being_created = false;
58}
59
60//----------------------------------------------------------------------
61// Destructor
62//----------------------------------------------------------------------
63Breakpoint::~Breakpoint()
64{
65}
66
67bool
68Breakpoint::IsInternal () const
69{
70    return LLDB_BREAK_ID_IS_INTERNAL(m_bid);
71}
72
73
74
75Target&
76Breakpoint::GetTarget ()
77{
78    return m_target;
79}
80
81const Target&
82Breakpoint::GetTarget () const
83{
84    return m_target;
85}
86
87BreakpointLocationSP
88Breakpoint::AddLocation (const Address &addr, bool *new_location)
89{
90    return m_locations.AddLocation (addr, new_location);
91}
92
93BreakpointLocationSP
94Breakpoint::FindLocationByAddress (const Address &addr)
95{
96    return m_locations.FindByAddress(addr);
97}
98
99break_id_t
100Breakpoint::FindLocationIDByAddress (const Address &addr)
101{
102    return m_locations.FindIDByAddress(addr);
103}
104
105BreakpointLocationSP
106Breakpoint::FindLocationByID (break_id_t bp_loc_id)
107{
108    return m_locations.FindByID(bp_loc_id);
109}
110
111BreakpointLocationSP
112Breakpoint::GetLocationAtIndex (size_t index)
113{
114    return m_locations.GetByIndex(index);
115}
116
117void
118Breakpoint::RemoveInvalidLocations (const ArchSpec &arch)
119{
120    m_locations.RemoveInvalidLocations(arch);
121}
122
123// For each of the overall options we need to decide how they propagate to
124// the location options.  This will determine the precedence of options on
125// the breakpoint vs. its locations.
126
127// Disable at the breakpoint level should override the location settings.
128// That way you can conveniently turn off a whole breakpoint without messing
129// up the individual settings.
130
131void
132Breakpoint::SetEnabled (bool enable)
133{
134    if (enable == m_options.IsEnabled())
135        return;
136
137    m_options.SetEnabled(enable);
138    if (enable)
139        m_locations.ResolveAllBreakpointSites();
140    else
141        m_locations.ClearAllBreakpointSites();
142
143    SendBreakpointChangedEvent (enable ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled);
144
145}
146
147bool
148Breakpoint::IsEnabled ()
149{
150    return m_options.IsEnabled();
151}
152
153void
154Breakpoint::SetIgnoreCount (uint32_t n)
155{
156    if (m_options.GetIgnoreCount() == n)
157        return;
158
159    m_options.SetIgnoreCount(n);
160    SendBreakpointChangedEvent (eBreakpointEventTypeIgnoreChanged);
161}
162
163void
164Breakpoint::DecrementIgnoreCount ()
165{
166    uint32_t ignore = m_options.GetIgnoreCount();
167    if (ignore != 0)
168        m_options.SetIgnoreCount(ignore - 1);
169}
170
171uint32_t
172Breakpoint::GetIgnoreCount () const
173{
174    return m_options.GetIgnoreCount();
175}
176
177bool
178Breakpoint::IgnoreCountShouldStop ()
179{
180    uint32_t ignore = GetIgnoreCount();
181    if (ignore != 0)
182    {
183        // When we get here we know the location that caused the stop doesn't have an ignore count,
184        // since by contract we call it first...  So we don't have to find & decrement it, we only have
185        // to decrement our own ignore count.
186        DecrementIgnoreCount();
187        return false;
188    }
189    else
190        return true;
191}
192
193uint32_t
194Breakpoint::GetHitCount () const
195{
196    return m_locations.GetHitCount();
197}
198
199bool
200Breakpoint::IsOneShot () const
201{
202    return m_options.IsOneShot();
203}
204
205void
206Breakpoint::SetOneShot (bool one_shot)
207{
208    m_options.SetOneShot (one_shot);
209}
210
211void
212Breakpoint::SetThreadID (lldb::tid_t thread_id)
213{
214    if (m_options.GetThreadSpec()->GetTID() == thread_id)
215        return;
216
217    m_options.GetThreadSpec()->SetTID(thread_id);
218    SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged);
219}
220
221lldb::tid_t
222Breakpoint::GetThreadID () const
223{
224    if (m_options.GetThreadSpecNoCreate() == NULL)
225        return LLDB_INVALID_THREAD_ID;
226    else
227        return m_options.GetThreadSpecNoCreate()->GetTID();
228}
229
230void
231Breakpoint::SetThreadIndex (uint32_t index)
232{
233    if (m_options.GetThreadSpec()->GetIndex() == index)
234        return;
235
236    m_options.GetThreadSpec()->SetIndex(index);
237    SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged);
238}
239
240uint32_t
241Breakpoint::GetThreadIndex() const
242{
243    if (m_options.GetThreadSpecNoCreate() == NULL)
244        return 0;
245    else
246        return m_options.GetThreadSpecNoCreate()->GetIndex();
247}
248
249void
250Breakpoint::SetThreadName (const char *thread_name)
251{
252    if (m_options.GetThreadSpec()->GetName() != NULL
253        && ::strcmp (m_options.GetThreadSpec()->GetName(), thread_name) == 0)
254        return;
255
256    m_options.GetThreadSpec()->SetName (thread_name);
257    SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged);
258}
259
260const char *
261Breakpoint::GetThreadName () const
262{
263    if (m_options.GetThreadSpecNoCreate() == NULL)
264        return NULL;
265    else
266        return m_options.GetThreadSpecNoCreate()->GetName();
267}
268
269void
270Breakpoint::SetQueueName (const char *queue_name)
271{
272    if (m_options.GetThreadSpec()->GetQueueName() != NULL
273        && ::strcmp (m_options.GetThreadSpec()->GetQueueName(), queue_name) == 0)
274        return;
275
276    m_options.GetThreadSpec()->SetQueueName (queue_name);
277    SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged);
278}
279
280const char *
281Breakpoint::GetQueueName () const
282{
283    if (m_options.GetThreadSpecNoCreate() == NULL)
284        return NULL;
285    else
286        return m_options.GetThreadSpecNoCreate()->GetQueueName();
287}
288
289void
290Breakpoint::SetCondition (const char *condition)
291{
292    m_options.SetCondition (condition);
293    SendBreakpointChangedEvent (eBreakpointEventTypeConditionChanged);
294}
295
296const char *
297Breakpoint::GetConditionText () const
298{
299    return m_options.GetConditionText();
300}
301
302// This function is used when "baton" doesn't need to be freed
303void
304Breakpoint::SetCallback (BreakpointHitCallback callback, void *baton, bool is_synchronous)
305{
306    // The default "Baton" class will keep a copy of "baton" and won't free
307    // or delete it when it goes goes out of scope.
308    m_options.SetCallback(callback, BatonSP (new Baton(baton)), is_synchronous);
309
310    SendBreakpointChangedEvent (eBreakpointEventTypeCommandChanged);
311}
312
313// This function is used when a baton needs to be freed and therefore is
314// contained in a "Baton" subclass.
315void
316Breakpoint::SetCallback (BreakpointHitCallback callback, const BatonSP &callback_baton_sp, bool is_synchronous)
317{
318    m_options.SetCallback(callback, callback_baton_sp, is_synchronous);
319}
320
321void
322Breakpoint::ClearCallback ()
323{
324    m_options.ClearCallback ();
325}
326
327bool
328Breakpoint::InvokeCallback (StoppointCallbackContext *context, break_id_t bp_loc_id)
329{
330    return m_options.InvokeCallback (context, GetID(), bp_loc_id);
331}
332
333BreakpointOptions *
334Breakpoint::GetOptions ()
335{
336    return &m_options;
337}
338
339void
340Breakpoint::ResolveBreakpoint ()
341{
342    if (m_resolver_sp)
343        m_resolver_sp->ResolveBreakpoint(*m_filter_sp);
344}
345
346void
347Breakpoint::ResolveBreakpointInModules (ModuleList &module_list)
348{
349    if (m_resolver_sp)
350        m_resolver_sp->ResolveBreakpointInModules(*m_filter_sp, module_list);
351}
352
353void
354Breakpoint::ClearAllBreakpointSites ()
355{
356    m_locations.ClearAllBreakpointSites();
357}
358
359//----------------------------------------------------------------------
360// ModulesChanged: Pass in a list of new modules, and
361//----------------------------------------------------------------------
362
363void
364Breakpoint::ModulesChanged (ModuleList &module_list, bool load, bool delete_locations)
365{
366    Mutex::Locker modules_mutex(module_list.GetMutex());
367    if (load)
368    {
369        // The logic for handling new modules is:
370        // 1) If the filter rejects this module, then skip it.
371        // 2) Run through the current location list and if there are any locations
372        //    for that module, we mark the module as "seen" and we don't try to re-resolve
373        //    breakpoint locations for that module.
374        //    However, we do add breakpoint sites to these locations if needed.
375        // 3) If we don't see this module in our breakpoint location list, call ResolveInModules.
376
377        ModuleList new_modules;  // We'll stuff the "unseen" modules in this list, and then resolve
378                                 // them after the locations pass.  Have to do it this way because
379                                 // resolving breakpoints will add new locations potentially.
380
381        const size_t num_locs = m_locations.GetSize();
382        size_t num_modules = module_list.GetSize();
383        for (size_t i = 0; i < num_modules; i++)
384        {
385            bool seen = false;
386            ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (i));
387            if (!m_filter_sp->ModulePasses (module_sp))
388                continue;
389
390            for (size_t loc_idx = 0; loc_idx < num_locs; loc_idx++)
391            {
392                BreakpointLocationSP break_loc = m_locations.GetByIndex(loc_idx);
393                if (!break_loc->IsEnabled())
394                    continue;
395                SectionSP section_sp (break_loc->GetAddress().GetSection());
396                if (!section_sp || section_sp->GetModule() == module_sp)
397                {
398                    if (!seen)
399                        seen = true;
400
401                    if (!break_loc->ResolveBreakpointSite())
402                    {
403                        Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
404                        if (log)
405                            log->Printf ("Warning: could not set breakpoint site for breakpoint location %d of breakpoint %d.\n",
406                                         break_loc->GetID(), GetID());
407                    }
408                }
409            }
410
411            if (!seen)
412                new_modules.AppendIfNeeded (module_sp);
413
414        }
415
416        if (new_modules.GetSize() > 0)
417        {
418            // If this is not an internal breakpoint, set up to record the new locations, then dispatch
419            // an event with the new locations.
420            if (!IsInternal())
421            {
422                BreakpointEventData *new_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded,
423                                                                                    shared_from_this());
424
425                m_locations.StartRecordingNewLocations(new_locations_event->GetBreakpointLocationCollection());
426
427                ResolveBreakpointInModules(new_modules);
428
429                m_locations.StopRecordingNewLocations();
430                if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0)
431                {
432                    SendBreakpointChangedEvent (new_locations_event);
433                }
434                else
435                    delete new_locations_event;
436            }
437            else
438                ResolveBreakpointInModules(new_modules);
439
440        }
441    }
442    else
443    {
444        // Go through the currently set locations and if any have breakpoints in
445        // the module list, then remove their breakpoint sites, and their locations if asked to.
446
447        BreakpointEventData *removed_locations_event;
448        if (!IsInternal())
449            removed_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsRemoved,
450                                                               shared_from_this());
451        else
452            removed_locations_event = NULL;
453
454        size_t num_modules = module_list.GetSize();
455        for (size_t i = 0; i < num_modules; i++)
456        {
457            ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (i));
458            if (m_filter_sp->ModulePasses (module_sp))
459            {
460                size_t loc_idx = 0;
461                size_t num_locations = m_locations.GetSize();
462                BreakpointLocationCollection locations_to_remove;
463                for (loc_idx = 0; loc_idx < num_locations; loc_idx++)
464                {
465                    BreakpointLocationSP break_loc_sp (m_locations.GetByIndex(loc_idx));
466                    SectionSP section_sp (break_loc_sp->GetAddress().GetSection());
467                    if (section_sp && section_sp->GetModule() == module_sp)
468                    {
469                        // Remove this breakpoint since the shared library is
470                        // unloaded, but keep the breakpoint location around
471                        // so we always get complete hit count and breakpoint
472                        // lifetime info
473                        break_loc_sp->ClearBreakpointSite();
474                        if (removed_locations_event)
475                        {
476                            removed_locations_event->GetBreakpointLocationCollection().Add(break_loc_sp);
477                        }
478                        if (delete_locations)
479                            locations_to_remove.Add (break_loc_sp);
480
481                    }
482                }
483
484                if (delete_locations)
485                {
486                    size_t num_locations_to_remove = locations_to_remove.GetSize();
487                    for (loc_idx = 0; loc_idx < num_locations_to_remove; loc_idx++)
488                        m_locations.RemoveLocation  (locations_to_remove.GetByIndex(loc_idx));
489                }
490            }
491        }
492        SendBreakpointChangedEvent (removed_locations_event);
493    }
494}
495
496void
497Breakpoint::ModuleReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp)
498{
499    ModuleList temp_list;
500    temp_list.Append (new_module_sp);
501    ModulesChanged (temp_list, true);
502
503    // TO DO: For now I'm just adding locations for the new module and removing the
504    // breakpoint locations that were in the old module.
505    // We should really go find the ones that are in the new module & if we can determine that they are "equivalent"
506    // carry over the options from the old location to the new.
507
508    temp_list.Clear();
509    temp_list.Append (old_module_sp);
510    ModulesChanged (temp_list, false, true);
511}
512
513void
514Breakpoint::Dump (Stream *)
515{
516}
517
518size_t
519Breakpoint::GetNumResolvedLocations() const
520{
521    // Return the number of breakpoints that are actually resolved and set
522    // down in the inferior process.
523    return m_locations.GetNumResolvedLocations();
524}
525
526size_t
527Breakpoint::GetNumLocations() const
528{
529    return m_locations.GetSize();
530}
531
532void
533Breakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_locations)
534{
535    assert (s != NULL);
536
537    if (!m_kind_description.empty())
538    {
539        if (eDescriptionLevelBrief)
540        {
541            s->PutCString (GetBreakpointKind());
542            return;
543        }
544        else
545            s->Printf("Kind: %s\n", GetBreakpointKind ());
546    }
547
548    const size_t num_locations = GetNumLocations ();
549    const size_t num_resolved_locations = GetNumResolvedLocations ();
550
551    // They just made the breakpoint, they don't need to be told HOW they made it...
552    // Also, we'll print the breakpoint number differently depending on whether there is 1 or more locations.
553    if (level != eDescriptionLevelInitial)
554    {
555        s->Printf("%i: ", GetID());
556        GetResolverDescription (s);
557        GetFilterDescription (s);
558    }
559
560    switch (level)
561    {
562    case lldb::eDescriptionLevelBrief:
563    case lldb::eDescriptionLevelFull:
564        if (num_locations > 0)
565        {
566            s->Printf(", locations = %" PRIu64, (uint64_t)num_locations);
567            if (num_resolved_locations > 0)
568                s->Printf(", resolved = %" PRIu64 ", hit count = %d", (uint64_t)num_resolved_locations, GetHitCount());
569        }
570        else
571        {
572            // Don't print the pending notification for exception resolvers since we don't generally
573            // know how to set them until the target is run.
574            if (m_resolver_sp->getResolverID() != BreakpointResolver::ExceptionResolver)
575                s->Printf(", locations = 0 (pending)");
576        }
577
578        GetOptions()->GetDescription(s, level);
579
580        if (level == lldb::eDescriptionLevelFull)
581        {
582            s->IndentLess();
583            s->EOL();
584        }
585        break;
586
587    case lldb::eDescriptionLevelInitial:
588        s->Printf ("Breakpoint %i: ", GetID());
589        if (num_locations == 0)
590        {
591            s->Printf ("no locations (pending).");
592        }
593        else if (num_locations == 1)
594        {
595            // If there is one location only, we'll just print that location information.  But don't do this if
596            // show locations is true, then that will be handled below.
597            if (show_locations == false)
598            {
599                GetLocationAtIndex(0)->GetDescription(s, level);
600            }
601            else
602            {
603                s->Printf ("%zd locations.", num_locations);
604            }
605        }
606        else
607        {
608            s->Printf ("%zd locations.", num_locations);
609        }
610        s->EOL();
611        break;
612    case lldb::eDescriptionLevelVerbose:
613        // Verbose mode does a debug dump of the breakpoint
614        Dump (s);
615        s->EOL ();
616            //s->Indent();
617        GetOptions()->GetDescription(s, level);
618        break;
619
620    default:
621        break;
622    }
623
624    // The brief description is just the location name (1.2 or whatever).  That's pointless to
625    // show in the breakpoint's description, so suppress it.
626    if (show_locations && level != lldb::eDescriptionLevelBrief)
627    {
628        s->IndentMore();
629        for (size_t i = 0; i < num_locations; ++i)
630        {
631            BreakpointLocation *loc = GetLocationAtIndex(i).get();
632            loc->GetDescription(s, level);
633            s->EOL();
634        }
635        s->IndentLess();
636    }
637}
638
639void
640Breakpoint::GetResolverDescription (Stream *s)
641{
642    if (m_resolver_sp)
643        m_resolver_sp->GetDescription (s);
644}
645
646
647bool
648Breakpoint::GetMatchingFileLine (const ConstString &filename, uint32_t line_number, BreakpointLocationCollection &loc_coll)
649{
650    // TODO: To be correct, this method needs to fill the breakpoint location collection
651    //       with the location IDs which match the filename and line_number.
652    //
653
654    if (m_resolver_sp)
655    {
656        BreakpointResolverFileLine *resolverFileLine = dyn_cast<BreakpointResolverFileLine>(m_resolver_sp.get());
657        if (resolverFileLine &&
658            resolverFileLine->m_file_spec.GetFilename() == filename &&
659            resolverFileLine->m_line_number == line_number)
660        {
661            return true;
662        }
663    }
664    return false;
665}
666
667void
668Breakpoint::GetFilterDescription (Stream *s)
669{
670    m_filter_sp->GetDescription (s);
671}
672
673void
674Breakpoint::SendBreakpointChangedEvent (lldb::BreakpointEventType eventKind)
675{
676    if (!m_being_created
677        && !IsInternal()
678        && GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged))
679    {
680        BreakpointEventData *data = new Breakpoint::BreakpointEventData (eventKind, shared_from_this());
681
682        GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, data);
683    }
684}
685
686void
687Breakpoint::SendBreakpointChangedEvent (BreakpointEventData *data)
688{
689
690    if (data == NULL)
691        return;
692
693    if (!m_being_created
694        && !IsInternal()
695        && GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged))
696        GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, data);
697    else
698        delete data;
699}
700
701Breakpoint::BreakpointEventData::BreakpointEventData (BreakpointEventType sub_type,
702                                                      const BreakpointSP &new_breakpoint_sp) :
703    EventData (),
704    m_breakpoint_event (sub_type),
705    m_new_breakpoint_sp (new_breakpoint_sp)
706{
707}
708
709Breakpoint::BreakpointEventData::~BreakpointEventData ()
710{
711}
712
713const ConstString &
714Breakpoint::BreakpointEventData::GetFlavorString ()
715{
716    static ConstString g_flavor ("Breakpoint::BreakpointEventData");
717    return g_flavor;
718}
719
720const ConstString &
721Breakpoint::BreakpointEventData::GetFlavor () const
722{
723    return BreakpointEventData::GetFlavorString ();
724}
725
726
727BreakpointSP &
728Breakpoint::BreakpointEventData::GetBreakpoint ()
729{
730    return m_new_breakpoint_sp;
731}
732
733BreakpointEventType
734Breakpoint::BreakpointEventData::GetBreakpointEventType () const
735{
736    return m_breakpoint_event;
737}
738
739void
740Breakpoint::BreakpointEventData::Dump (Stream *s) const
741{
742}
743
744const Breakpoint::BreakpointEventData *
745Breakpoint::BreakpointEventData::GetEventDataFromEvent (const Event *event)
746{
747    if (event)
748    {
749        const EventData *event_data = event->GetData();
750        if (event_data && event_data->GetFlavor() == BreakpointEventData::GetFlavorString())
751            return static_cast <const BreakpointEventData *> (event->GetData());
752    }
753    return NULL;
754}
755
756BreakpointEventType
757Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (const EventSP &event_sp)
758{
759    const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get());
760
761    if (data == NULL)
762        return eBreakpointEventTypeInvalidType;
763    else
764        return data->GetBreakpointEventType();
765}
766
767BreakpointSP
768Breakpoint::BreakpointEventData::GetBreakpointFromEvent (const EventSP &event_sp)
769{
770    BreakpointSP bp_sp;
771
772    const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get());
773    if (data)
774        bp_sp = data->m_new_breakpoint_sp;
775
776    return bp_sp;
777}
778
779size_t
780Breakpoint::BreakpointEventData::GetNumBreakpointLocationsFromEvent (const EventSP &event_sp)
781{
782    const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get());
783    if (data)
784        return data->m_locations.GetSize();
785
786    return 0;
787}
788
789lldb::BreakpointLocationSP
790Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent (const lldb::EventSP &event_sp, uint32_t bp_loc_idx)
791{
792    lldb::BreakpointLocationSP bp_loc_sp;
793
794    const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get());
795    if (data)
796    {
797        bp_loc_sp = data->m_locations.GetByIndex(bp_loc_idx);
798    }
799
800    return bp_loc_sp;
801}
802