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