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