1 //===-- TargetList.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 // C Includes 11 // C++ Includes 12 // Other libraries and framework includes 13 #include "llvm/ADT/SmallString.h" 14 15 // Project includes 16 #include "lldb/Core/Broadcaster.h" 17 #include "lldb/Core/Debugger.h" 18 #include "lldb/Core/Event.h" 19 #include "lldb/Core/Module.h" 20 #include "lldb/Core/ModuleSpec.h" 21 #include "lldb/Core/State.h" 22 #include "lldb/Core/Timer.h" 23 #include "lldb/Host/Host.h" 24 #include "lldb/Host/HostInfo.h" 25 #include "lldb/Interpreter/CommandInterpreter.h" 26 #include "lldb/Interpreter/OptionGroupPlatform.h" 27 #include "lldb/Symbol/ObjectFile.h" 28 #include "lldb/Target/Platform.h" 29 #include "lldb/Target/Process.h" 30 #include "lldb/Target/TargetList.h" 31 32 using namespace lldb; 33 using namespace lldb_private; 34 35 ConstString & 36 TargetList::GetStaticBroadcasterClass () 37 { 38 static ConstString class_name ("lldb.targetList"); 39 return class_name; 40 } 41 42 //---------------------------------------------------------------------- 43 // TargetList constructor 44 //---------------------------------------------------------------------- 45 TargetList::TargetList(Debugger &debugger) 46 : Broadcaster(debugger.GetBroadcasterManager(), TargetList::GetStaticBroadcasterClass().AsCString()), 47 m_target_list(), 48 m_target_list_mutex(), 49 m_selected_target_idx(0) 50 { 51 CheckInWithManager(); 52 } 53 54 //---------------------------------------------------------------------- 55 // Destructor 56 //---------------------------------------------------------------------- 57 TargetList::~TargetList() 58 { 59 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 60 m_target_list.clear(); 61 } 62 63 Error 64 TargetList::CreateTarget (Debugger &debugger, 65 const char *user_exe_path, 66 const char *triple_cstr, 67 bool get_dependent_files, 68 const OptionGroupPlatform *platform_options, 69 TargetSP &target_sp) 70 { 71 return CreateTargetInternal (debugger, 72 user_exe_path, 73 triple_cstr, 74 get_dependent_files, 75 platform_options, 76 target_sp, 77 false); 78 } 79 80 Error 81 TargetList::CreateTarget (Debugger &debugger, 82 const char *user_exe_path, 83 const ArchSpec& specified_arch, 84 bool get_dependent_files, 85 PlatformSP &platform_sp, 86 TargetSP &target_sp) 87 { 88 return CreateTargetInternal (debugger, 89 user_exe_path, 90 specified_arch, 91 get_dependent_files, 92 platform_sp, 93 target_sp, 94 false); 95 } 96 97 Error 98 TargetList::CreateTargetInternal (Debugger &debugger, 99 const char *user_exe_path, 100 const char *triple_cstr, 101 bool get_dependent_files, 102 const OptionGroupPlatform *platform_options, 103 TargetSP &target_sp, 104 bool is_dummy_target) 105 { 106 Error error; 107 PlatformSP platform_sp; 108 109 // This is purposely left empty unless it is specified by triple_cstr. 110 // If not initialized via triple_cstr, then the currently selected platform 111 // will set the architecture correctly. 112 const ArchSpec arch(triple_cstr); 113 if (triple_cstr && triple_cstr[0]) 114 { 115 if (!arch.IsValid()) 116 { 117 error.SetErrorStringWithFormat("invalid triple '%s'", triple_cstr); 118 return error; 119 } 120 } 121 122 ArchSpec platform_arch(arch); 123 124 bool prefer_platform_arch = false; 125 126 CommandInterpreter &interpreter = debugger.GetCommandInterpreter(); 127 128 // let's see if there is already an existing plaform before we go creating another... 129 platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); 130 131 if (platform_options && platform_options->PlatformWasSpecified ()) 132 { 133 // Create a new platform if it doesn't match the selected platform 134 if (!platform_options->PlatformMatches(platform_sp)) 135 { 136 const bool select_platform = true; 137 platform_sp = platform_options->CreatePlatformWithOptions (interpreter, 138 arch, 139 select_platform, 140 error, 141 platform_arch); 142 if (!platform_sp) 143 return error; 144 } 145 } 146 147 if (user_exe_path && user_exe_path[0]) 148 { 149 ModuleSpecList module_specs; 150 ModuleSpec module_spec; 151 module_spec.GetFileSpec().SetFile(user_exe_path, true); 152 153 // Resolve the executable in case we are given a path to a application bundle 154 // like a .app bundle on MacOSX 155 Host::ResolveExecutableInBundle (module_spec.GetFileSpec()); 156 157 lldb::offset_t file_offset = 0; 158 lldb::offset_t file_size = 0; 159 const size_t num_specs = ObjectFile::GetModuleSpecifications (module_spec.GetFileSpec(), file_offset, file_size, module_specs); 160 if (num_specs > 0) 161 { 162 ModuleSpec matching_module_spec; 163 164 if (num_specs == 1) 165 { 166 if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec)) 167 { 168 if (platform_arch.IsValid()) 169 { 170 if (platform_arch.IsCompatibleMatch(matching_module_spec.GetArchitecture())) 171 { 172 // If the OS or vendor weren't specified, then adopt the module's 173 // architecture so that the platform matching can be more accurate 174 if (!platform_arch.TripleOSWasSpecified() || !platform_arch.TripleVendorWasSpecified()) 175 { 176 prefer_platform_arch = true; 177 platform_arch = matching_module_spec.GetArchitecture(); 178 } 179 } 180 else 181 { 182 StreamString platform_arch_strm; 183 StreamString module_arch_strm; 184 185 platform_arch.DumpTriple(platform_arch_strm); 186 matching_module_spec.GetArchitecture().DumpTriple(module_arch_strm); 187 error.SetErrorStringWithFormat("the specified architecture '%s' is not compatible with '%s' in '%s'", 188 platform_arch_strm.GetString().c_str(), 189 module_arch_strm.GetString().c_str(), 190 module_spec.GetFileSpec().GetPath().c_str()); 191 return error; 192 } 193 } 194 else 195 { 196 // Only one arch and none was specified 197 prefer_platform_arch = true; 198 platform_arch = matching_module_spec.GetArchitecture(); 199 } 200 } 201 } 202 else 203 { 204 if (arch.IsValid()) 205 { 206 module_spec.GetArchitecture() = arch; 207 if (module_specs.FindMatchingModuleSpec(module_spec, matching_module_spec)) 208 { 209 prefer_platform_arch = true; 210 platform_arch = matching_module_spec.GetArchitecture(); 211 } 212 } 213 else 214 { 215 // No architecture specified, check if there is only one platform for 216 // all of the architectures. 217 218 typedef std::vector<PlatformSP> PlatformList; 219 PlatformList platforms; 220 PlatformSP host_platform_sp = Platform::GetHostPlatform(); 221 for (size_t i=0; i<num_specs; ++i) 222 { 223 ModuleSpec module_spec; 224 if (module_specs.GetModuleSpecAtIndex(i, module_spec)) 225 { 226 // See if there was a selected platform and check that first 227 // since the user may have specified it. 228 if (platform_sp) 229 { 230 if (platform_sp->IsCompatibleArchitecture(module_spec.GetArchitecture(), false, nullptr)) 231 { 232 platforms.push_back(platform_sp); 233 continue; 234 } 235 } 236 237 // Next check the host platform it if wasn't already checked above 238 if (host_platform_sp && (!platform_sp || host_platform_sp->GetName() != platform_sp->GetName())) 239 { 240 if (host_platform_sp->IsCompatibleArchitecture(module_spec.GetArchitecture(), false, nullptr)) 241 { 242 platforms.push_back(host_platform_sp); 243 continue; 244 } 245 } 246 247 // Just find a platform that matches the architecture in the executable file 248 PlatformSP fallback_platform_sp (Platform::GetPlatformForArchitecture(module_spec.GetArchitecture(), nullptr)); 249 if (fallback_platform_sp) 250 { 251 platforms.push_back(fallback_platform_sp); 252 } 253 } 254 } 255 256 Platform *platform_ptr = nullptr; 257 bool more_than_one_platforms = false; 258 for (const auto &the_platform_sp : platforms) 259 { 260 if (platform_ptr) 261 { 262 if (platform_ptr->GetName() != the_platform_sp->GetName()) 263 { 264 more_than_one_platforms = true; 265 platform_ptr = nullptr; 266 break; 267 } 268 } 269 else 270 { 271 platform_ptr = the_platform_sp.get(); 272 } 273 } 274 275 if (platform_ptr) 276 { 277 // All platforms for all modules in the exectuable match, so we can select this platform 278 platform_sp = platforms.front(); 279 } 280 else if (more_than_one_platforms == false) 281 { 282 // No platforms claim to support this file 283 error.SetErrorString ("No matching platforms found for this file, specify one with the --platform option"); 284 return error; 285 } 286 else 287 { 288 // More than one platform claims to support this file, so the --platform option must be specified 289 StreamString error_strm; 290 std::set<Platform *> platform_set; 291 error_strm.Printf ("more than one platform supports this executable ("); 292 for (const auto &the_platform_sp : platforms) 293 { 294 if (platform_set.find(the_platform_sp.get()) == platform_set.end()) 295 { 296 if (!platform_set.empty()) 297 error_strm.PutCString(", "); 298 error_strm.PutCString(the_platform_sp->GetName().GetCString()); 299 platform_set.insert(the_platform_sp.get()); 300 } 301 } 302 error_strm.Printf("), use the --platform option to specify a platform"); 303 error.SetErrorString(error_strm.GetString().c_str()); 304 return error; 305 } 306 } 307 } 308 } 309 } 310 311 // If we have a valid architecture, make sure the current platform is 312 // compatible with that architecture 313 if (!prefer_platform_arch && arch.IsValid()) 314 { 315 if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) 316 { 317 platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch); 318 if (!is_dummy_target && platform_sp) 319 debugger.GetPlatformList().SetSelectedPlatform(platform_sp); 320 } 321 } 322 else if (platform_arch.IsValid()) 323 { 324 // if "arch" isn't valid, yet "platform_arch" is, it means we have an executable file with 325 // a single architecture which should be used 326 ArchSpec fixed_platform_arch; 327 if (!platform_sp->IsCompatibleArchitecture(platform_arch, false, &fixed_platform_arch)) 328 { 329 platform_sp = Platform::GetPlatformForArchitecture(platform_arch, &fixed_platform_arch); 330 if (!is_dummy_target && platform_sp) 331 debugger.GetPlatformList().SetSelectedPlatform(platform_sp); 332 } 333 } 334 335 if (!platform_arch.IsValid()) 336 platform_arch = arch; 337 338 error = TargetList::CreateTargetInternal (debugger, 339 user_exe_path, 340 platform_arch, 341 get_dependent_files, 342 platform_sp, 343 target_sp, 344 is_dummy_target); 345 return error; 346 } 347 348 lldb::TargetSP 349 TargetList::GetDummyTarget (lldb_private::Debugger &debugger) 350 { 351 // FIXME: Maybe the dummy target should be per-Debugger 352 if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid()) 353 { 354 ArchSpec arch(Target::GetDefaultArchitecture()); 355 if (!arch.IsValid()) 356 arch = HostInfo::GetArchitecture(); 357 Error err = CreateDummyTarget(debugger, 358 arch.GetTriple().getTriple().c_str(), 359 m_dummy_target_sp); 360 } 361 362 return m_dummy_target_sp; 363 } 364 365 Error 366 TargetList::CreateDummyTarget (Debugger &debugger, 367 const char *specified_arch_name, 368 lldb::TargetSP &target_sp) 369 { 370 PlatformSP host_platform_sp(Platform::GetHostPlatform()); 371 return CreateTargetInternal (debugger, 372 (const char *) nullptr, 373 specified_arch_name, 374 false, 375 (const OptionGroupPlatform *) nullptr, 376 target_sp, 377 true); 378 } 379 380 Error 381 TargetList::CreateTargetInternal (Debugger &debugger, 382 const char *user_exe_path, 383 const ArchSpec& specified_arch, 384 bool get_dependent_files, 385 lldb::PlatformSP &platform_sp, 386 lldb::TargetSP &target_sp, 387 bool is_dummy_target) 388 { 389 Timer scoped_timer (__PRETTY_FUNCTION__, 390 "TargetList::CreateTarget (file = '%s', arch = '%s')", 391 user_exe_path, 392 specified_arch.GetArchitectureName()); 393 Error error; 394 395 ArchSpec arch(specified_arch); 396 397 if (arch.IsValid()) 398 { 399 if (!platform_sp || !platform_sp->IsCompatibleArchitecture(arch, false, nullptr)) 400 platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch); 401 } 402 403 if (!platform_sp) 404 platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); 405 406 if (!arch.IsValid()) 407 arch = specified_arch; 408 409 FileSpec file (user_exe_path, false); 410 if (!file.Exists() && user_exe_path && user_exe_path[0] == '~') 411 { 412 // we want to expand the tilde but we don't want to resolve any symbolic links 413 // so we can't use the FileSpec constructor's resolve flag 414 llvm::SmallString<64> unglobbed_path(user_exe_path); 415 FileSpec::ResolveUsername(unglobbed_path); 416 417 if (unglobbed_path.empty()) 418 file = FileSpec(user_exe_path, false); 419 else 420 file = FileSpec(unglobbed_path.c_str(), false); 421 } 422 423 bool user_exe_path_is_bundle = false; 424 char resolved_bundle_exe_path[PATH_MAX]; 425 resolved_bundle_exe_path[0] = '\0'; 426 if (file) 427 { 428 if (file.GetFileType() == FileSpec::eFileTypeDirectory) 429 user_exe_path_is_bundle = true; 430 431 if (file.IsRelative() && user_exe_path) 432 { 433 // Ignore paths that start with "./" and "../" 434 if (!((user_exe_path[0] == '.' && user_exe_path[1] == '/') || 435 (user_exe_path[0] == '.' && user_exe_path[1] == '.' && user_exe_path[2] == '/'))) 436 { 437 char cwd[PATH_MAX]; 438 if (getcwd (cwd, sizeof(cwd))) 439 { 440 std::string cwd_user_exe_path (cwd); 441 cwd_user_exe_path += '/'; 442 cwd_user_exe_path += user_exe_path; 443 FileSpec cwd_file (cwd_user_exe_path.c_str(), false); 444 if (cwd_file.Exists()) 445 file = cwd_file; 446 } 447 } 448 } 449 450 ModuleSP exe_module_sp; 451 if (platform_sp) 452 { 453 FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths()); 454 ModuleSpec module_spec(file, arch); 455 error = platform_sp->ResolveExecutable(module_spec, 456 exe_module_sp, 457 executable_search_paths.GetSize() ? &executable_search_paths : nullptr); 458 } 459 460 if (error.Success() && exe_module_sp) 461 { 462 if (exe_module_sp->GetObjectFile() == nullptr) 463 { 464 if (arch.IsValid()) 465 { 466 error.SetErrorStringWithFormat("\"%s\" doesn't contain architecture %s", 467 file.GetPath().c_str(), 468 arch.GetArchitectureName()); 469 } 470 else 471 { 472 error.SetErrorStringWithFormat("unsupported file type \"%s\"", 473 file.GetPath().c_str()); 474 } 475 return error; 476 } 477 target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); 478 target_sp->SetExecutableModule (exe_module_sp, get_dependent_files); 479 if (user_exe_path_is_bundle) 480 exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, sizeof(resolved_bundle_exe_path)); 481 } 482 } 483 else 484 { 485 // No file was specified, just create an empty target with any arch 486 // if a valid arch was specified 487 target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); 488 } 489 490 if (target_sp) 491 { 492 // Set argv0 with what the user typed, unless the user specified a 493 // directory. If the user specified a directory, then it is probably a 494 // bundle that was resolved and we need to use the resolved bundle path 495 if (user_exe_path) 496 { 497 // Use exactly what the user typed as the first argument when we exec or posix_spawn 498 if (user_exe_path_is_bundle && resolved_bundle_exe_path[0]) 499 { 500 target_sp->SetArg0 (resolved_bundle_exe_path); 501 } 502 else 503 { 504 // Use resolved path 505 target_sp->SetArg0 (file.GetPath().c_str()); 506 } 507 } 508 if (file.GetDirectory()) 509 { 510 FileSpec file_dir; 511 file_dir.GetDirectory() = file.GetDirectory(); 512 target_sp->GetExecutableSearchPaths ().Append (file_dir); 513 } 514 515 // Don't put the dummy target in the target list, it's held separately. 516 if (!is_dummy_target) 517 { 518 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 519 m_selected_target_idx = m_target_list.size(); 520 m_target_list.push_back(target_sp); 521 // Now prime this from the dummy target: 522 target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget()); 523 } 524 else 525 { 526 m_dummy_target_sp = target_sp; 527 } 528 } 529 530 return error; 531 } 532 533 bool 534 TargetList::DeleteTarget (TargetSP &target_sp) 535 { 536 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 537 collection::iterator pos, end = m_target_list.end(); 538 539 for (pos = m_target_list.begin(); pos != end; ++pos) 540 { 541 if (pos->get() == target_sp.get()) 542 { 543 m_target_list.erase(pos); 544 return true; 545 } 546 } 547 return false; 548 } 549 550 TargetSP 551 TargetList::FindTargetWithExecutableAndArchitecture(const FileSpec &exe_file_spec, 552 const ArchSpec *exe_arch_ptr) const 553 { 554 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 555 TargetSP target_sp; 556 bool full_match = (bool)exe_file_spec.GetDirectory(); 557 558 collection::const_iterator pos, end = m_target_list.end(); 559 for (pos = m_target_list.begin(); pos != end; ++pos) 560 { 561 Module *exe_module = (*pos)->GetExecutableModulePointer(); 562 563 if (exe_module) 564 { 565 if (FileSpec::Equal (exe_file_spec, exe_module->GetFileSpec(), full_match)) 566 { 567 if (exe_arch_ptr) 568 { 569 if (!exe_arch_ptr->IsCompatibleMatch(exe_module->GetArchitecture())) 570 continue; 571 } 572 target_sp = *pos; 573 break; 574 } 575 } 576 } 577 return target_sp; 578 } 579 580 TargetSP 581 TargetList::FindTargetWithProcessID (lldb::pid_t pid) const 582 { 583 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 584 TargetSP target_sp; 585 collection::const_iterator pos, end = m_target_list.end(); 586 for (pos = m_target_list.begin(); pos != end; ++pos) 587 { 588 Process* process = (*pos)->GetProcessSP().get(); 589 if (process && process->GetID() == pid) 590 { 591 target_sp = *pos; 592 break; 593 } 594 } 595 return target_sp; 596 } 597 598 TargetSP 599 TargetList::FindTargetWithProcess (Process *process) const 600 { 601 TargetSP target_sp; 602 if (process) 603 { 604 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 605 collection::const_iterator pos, end = m_target_list.end(); 606 for (pos = m_target_list.begin(); pos != end; ++pos) 607 { 608 if (process == (*pos)->GetProcessSP().get()) 609 { 610 target_sp = *pos; 611 break; 612 } 613 } 614 } 615 return target_sp; 616 } 617 618 TargetSP 619 TargetList::GetTargetSP (Target *target) const 620 { 621 TargetSP target_sp; 622 if (target) 623 { 624 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 625 collection::const_iterator pos, end = m_target_list.end(); 626 for (pos = m_target_list.begin(); pos != end; ++pos) 627 { 628 if (target == (*pos).get()) 629 { 630 target_sp = *pos; 631 break; 632 } 633 } 634 } 635 return target_sp; 636 } 637 638 uint32_t 639 TargetList::SendAsyncInterrupt (lldb::pid_t pid) 640 { 641 uint32_t num_async_interrupts_sent = 0; 642 643 if (pid != LLDB_INVALID_PROCESS_ID) 644 { 645 TargetSP target_sp(FindTargetWithProcessID (pid)); 646 if (target_sp) 647 { 648 Process* process = target_sp->GetProcessSP().get(); 649 if (process) 650 { 651 process->SendAsyncInterrupt(); 652 ++num_async_interrupts_sent; 653 } 654 } 655 } 656 else 657 { 658 // We don't have a valid pid to broadcast to, so broadcast to the target 659 // list's async broadcaster... 660 BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); 661 } 662 663 return num_async_interrupts_sent; 664 } 665 666 uint32_t 667 TargetList::SignalIfRunning (lldb::pid_t pid, int signo) 668 { 669 uint32_t num_signals_sent = 0; 670 Process *process = nullptr; 671 if (pid == LLDB_INVALID_PROCESS_ID) 672 { 673 // Signal all processes with signal 674 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 675 collection::iterator pos, end = m_target_list.end(); 676 for (pos = m_target_list.begin(); pos != end; ++pos) 677 { 678 process = (*pos)->GetProcessSP().get(); 679 if (process) 680 { 681 if (process->IsAlive()) 682 { 683 ++num_signals_sent; 684 process->Signal (signo); 685 } 686 } 687 } 688 } 689 else 690 { 691 // Signal a specific process with signal 692 TargetSP target_sp(FindTargetWithProcessID (pid)); 693 if (target_sp) 694 { 695 process = target_sp->GetProcessSP().get(); 696 if (process) 697 { 698 if (process->IsAlive()) 699 { 700 ++num_signals_sent; 701 process->Signal (signo); 702 } 703 } 704 } 705 } 706 return num_signals_sent; 707 } 708 709 int 710 TargetList::GetNumTargets () const 711 { 712 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 713 return m_target_list.size(); 714 } 715 716 lldb::TargetSP 717 TargetList::GetTargetAtIndex (uint32_t idx) const 718 { 719 TargetSP target_sp; 720 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 721 if (idx < m_target_list.size()) 722 target_sp = m_target_list[idx]; 723 return target_sp; 724 } 725 726 uint32_t 727 TargetList::GetIndexOfTarget (lldb::TargetSP target_sp) const 728 { 729 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 730 size_t num_targets = m_target_list.size(); 731 for (size_t idx = 0; idx < num_targets; idx++) 732 { 733 if (target_sp == m_target_list[idx]) 734 return idx; 735 } 736 return UINT32_MAX; 737 } 738 739 uint32_t 740 TargetList::SetSelectedTarget (Target* target) 741 { 742 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 743 collection::const_iterator pos, 744 begin = m_target_list.begin(), 745 end = m_target_list.end(); 746 for (pos = begin; pos != end; ++pos) 747 { 748 if (pos->get() == target) 749 { 750 m_selected_target_idx = std::distance (begin, pos); 751 return m_selected_target_idx; 752 } 753 } 754 m_selected_target_idx = 0; 755 return m_selected_target_idx; 756 } 757 758 lldb::TargetSP 759 TargetList::GetSelectedTarget () 760 { 761 std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); 762 if (m_selected_target_idx >= m_target_list.size()) 763 m_selected_target_idx = 0; 764 return GetTargetAtIndex (m_selected_target_idx); 765 } 766