| //===-- TargetList.cpp ------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| // Project includes |
| #include "lldb/Target/TargetList.h" |
| #include "lldb/Core/Broadcaster.h" |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/Event.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Core/ModuleSpec.h" |
| #include "lldb/Core/State.h" |
| #include "lldb/Host/Host.h" |
| #include "lldb/Host/HostInfo.h" |
| #include "lldb/Interpreter/CommandInterpreter.h" |
| #include "lldb/Interpreter/OptionGroupPlatform.h" |
| #include "lldb/Symbol/ObjectFile.h" |
| #include "lldb/Target/Platform.h" |
| #include "lldb/Target/Process.h" |
| #include "lldb/Utility/TildeExpressionResolver.h" |
| #include "lldb/Utility/Timer.h" |
| |
| // Other libraries and framework includes |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/Support/FileSystem.h" |
| |
| using namespace lldb; |
| using namespace lldb_private; |
| |
| ConstString &TargetList::GetStaticBroadcasterClass() { |
| static ConstString class_name("lldb.targetList"); |
| return class_name; |
| } |
| |
| //---------------------------------------------------------------------- |
| // TargetList constructor |
| //---------------------------------------------------------------------- |
| TargetList::TargetList(Debugger &debugger) |
| : Broadcaster(debugger.GetBroadcasterManager(), |
| TargetList::GetStaticBroadcasterClass().AsCString()), |
| m_target_list(), m_target_list_mutex(), m_selected_target_idx(0) { |
| CheckInWithManager(); |
| } |
| |
| //---------------------------------------------------------------------- |
| // Destructor |
| //---------------------------------------------------------------------- |
| TargetList::~TargetList() { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| m_target_list.clear(); |
| } |
| |
| Status TargetList::CreateTarget(Debugger &debugger, |
| llvm::StringRef user_exe_path, |
| llvm::StringRef triple_str, |
| bool get_dependent_files, |
| const OptionGroupPlatform *platform_options, |
| TargetSP &target_sp) { |
| return CreateTargetInternal(debugger, user_exe_path, triple_str, |
| get_dependent_files, platform_options, target_sp, |
| false); |
| } |
| |
| Status TargetList::CreateTarget(Debugger &debugger, |
| llvm::StringRef user_exe_path, |
| const ArchSpec &specified_arch, |
| bool get_dependent_files, |
| PlatformSP &platform_sp, TargetSP &target_sp) { |
| return CreateTargetInternal(debugger, user_exe_path, specified_arch, |
| get_dependent_files, platform_sp, target_sp, |
| false); |
| } |
| |
| Status TargetList::CreateTargetInternal( |
| Debugger &debugger, llvm::StringRef user_exe_path, |
| llvm::StringRef triple_str, bool get_dependent_files, |
| const OptionGroupPlatform *platform_options, TargetSP &target_sp, |
| bool is_dummy_target) { |
| Status error; |
| PlatformSP platform_sp; |
| |
| // This is purposely left empty unless it is specified by triple_cstr. If not |
| // initialized via triple_cstr, then the currently selected platform will set |
| // the architecture correctly. |
| const ArchSpec arch(triple_str); |
| if (!triple_str.empty()) { |
| if (!arch.IsValid()) { |
| error.SetErrorStringWithFormat("invalid triple '%s'", |
| triple_str.str().c_str()); |
| return error; |
| } |
| } |
| |
| ArchSpec platform_arch(arch); |
| |
| bool prefer_platform_arch = false; |
| |
| CommandInterpreter &interpreter = debugger.GetCommandInterpreter(); |
| |
| // let's see if there is already an existing platform before we go creating |
| // another... |
| platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); |
| |
| if (platform_options && platform_options->PlatformWasSpecified()) { |
| // Create a new platform if it doesn't match the selected platform |
| if (!platform_options->PlatformMatches(platform_sp)) { |
| const bool select_platform = true; |
| platform_sp = platform_options->CreatePlatformWithOptions( |
| interpreter, arch, select_platform, error, platform_arch); |
| if (!platform_sp) |
| return error; |
| } |
| } |
| |
| if (!user_exe_path.empty()) { |
| ModuleSpecList module_specs; |
| ModuleSpec module_spec; |
| module_spec.GetFileSpec().SetFile(user_exe_path, true, |
| FileSpec::Style::native); |
| |
| // Resolve the executable in case we are given a path to a application |
| // bundle like a .app bundle on MacOSX |
| Host::ResolveExecutableInBundle(module_spec.GetFileSpec()); |
| |
| lldb::offset_t file_offset = 0; |
| lldb::offset_t file_size = 0; |
| const size_t num_specs = ObjectFile::GetModuleSpecifications( |
| module_spec.GetFileSpec(), file_offset, file_size, module_specs); |
| if (num_specs > 0) { |
| ModuleSpec matching_module_spec; |
| |
| if (num_specs == 1) { |
| if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec)) { |
| if (platform_arch.IsValid()) { |
| if (platform_arch.IsCompatibleMatch( |
| matching_module_spec.GetArchitecture())) { |
| // If the OS or vendor weren't specified, then adopt the module's |
| // architecture so that the platform matching can be more |
| // accurate |
| if (!platform_arch.TripleOSWasSpecified() || |
| !platform_arch.TripleVendorWasSpecified()) { |
| prefer_platform_arch = true; |
| platform_arch = matching_module_spec.GetArchitecture(); |
| } |
| } else { |
| StreamString platform_arch_strm; |
| StreamString module_arch_strm; |
| |
| platform_arch.DumpTriple(platform_arch_strm); |
| matching_module_spec.GetArchitecture().DumpTriple( |
| module_arch_strm); |
| error.SetErrorStringWithFormat( |
| "the specified architecture '%s' is not compatible with '%s' " |
| "in '%s'", |
| platform_arch_strm.GetData(), module_arch_strm.GetData(), |
| module_spec.GetFileSpec().GetPath().c_str()); |
| return error; |
| } |
| } else { |
| // Only one arch and none was specified |
| prefer_platform_arch = true; |
| platform_arch = matching_module_spec.GetArchitecture(); |
| } |
| } |
| } else { |
| if (arch.IsValid()) { |
| module_spec.GetArchitecture() = arch; |
| if (module_specs.FindMatchingModuleSpec(module_spec, |
| matching_module_spec)) { |
| prefer_platform_arch = true; |
| platform_arch = matching_module_spec.GetArchitecture(); |
| } |
| } else { |
| // No architecture specified, check if there is only one platform for |
| // all of the architectures. |
| |
| typedef std::vector<PlatformSP> PlatformList; |
| PlatformList platforms; |
| PlatformSP host_platform_sp = Platform::GetHostPlatform(); |
| for (size_t i = 0; i < num_specs; ++i) { |
| ModuleSpec module_spec; |
| if (module_specs.GetModuleSpecAtIndex(i, module_spec)) { |
| // See if there was a selected platform and check that first |
| // since the user may have specified it. |
| if (platform_sp) { |
| if (platform_sp->IsCompatibleArchitecture( |
| module_spec.GetArchitecture(), false, nullptr)) { |
| platforms.push_back(platform_sp); |
| continue; |
| } |
| } |
| |
| // Next check the host platform it if wasn't already checked |
| // above |
| if (host_platform_sp && |
| (!platform_sp || |
| host_platform_sp->GetName() != platform_sp->GetName())) { |
| if (host_platform_sp->IsCompatibleArchitecture( |
| module_spec.GetArchitecture(), false, nullptr)) { |
| platforms.push_back(host_platform_sp); |
| continue; |
| } |
| } |
| |
| // Just find a platform that matches the architecture in the |
| // executable file |
| PlatformSP fallback_platform_sp( |
| Platform::GetPlatformForArchitecture( |
| module_spec.GetArchitecture(), nullptr)); |
| if (fallback_platform_sp) { |
| platforms.push_back(fallback_platform_sp); |
| } |
| } |
| } |
| |
| Platform *platform_ptr = nullptr; |
| bool more_than_one_platforms = false; |
| for (const auto &the_platform_sp : platforms) { |
| if (platform_ptr) { |
| if (platform_ptr->GetName() != the_platform_sp->GetName()) { |
| more_than_one_platforms = true; |
| platform_ptr = nullptr; |
| break; |
| } |
| } else { |
| platform_ptr = the_platform_sp.get(); |
| } |
| } |
| |
| if (platform_ptr) { |
| // All platforms for all modules in the executable match, so we can |
| // select this platform |
| platform_sp = platforms.front(); |
| } else if (more_than_one_platforms == false) { |
| // No platforms claim to support this file |
| error.SetErrorString("No matching platforms found for this file, " |
| "specify one with the --platform option"); |
| return error; |
| } else { |
| // More than one platform claims to support this file, so the |
| // --platform option must be specified |
| StreamString error_strm; |
| std::set<Platform *> platform_set; |
| error_strm.Printf( |
| "more than one platform supports this executable ("); |
| for (const auto &the_platform_sp : platforms) { |
| if (platform_set.find(the_platform_sp.get()) == |
| platform_set.end()) { |
| if (!platform_set.empty()) |
| error_strm.PutCString(", "); |
| error_strm.PutCString(the_platform_sp->GetName().GetCString()); |
| platform_set.insert(the_platform_sp.get()); |
| } |
| } |
| error_strm.Printf( |
| "), use the --platform option to specify a platform"); |
| error.SetErrorString(error_strm.GetString()); |
| return error; |
| } |
| } |
| } |
| } |
| } |
| |
| // If we have a valid architecture, make sure the current platform is |
| // compatible with that architecture |
| if (!prefer_platform_arch && arch.IsValid()) { |
| if (!platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch)) { |
| platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch); |
| if (!is_dummy_target && platform_sp) |
| debugger.GetPlatformList().SetSelectedPlatform(platform_sp); |
| } |
| } else if (platform_arch.IsValid()) { |
| // if "arch" isn't valid, yet "platform_arch" is, it means we have an |
| // executable file with a single architecture which should be used |
| ArchSpec fixed_platform_arch; |
| if (!platform_sp->IsCompatibleArchitecture(platform_arch, false, |
| &fixed_platform_arch)) { |
| platform_sp = Platform::GetPlatformForArchitecture(platform_arch, |
| &fixed_platform_arch); |
| if (!is_dummy_target && platform_sp) |
| debugger.GetPlatformList().SetSelectedPlatform(platform_sp); |
| } |
| } |
| |
| if (!platform_arch.IsValid()) |
| platform_arch = arch; |
| |
| error = TargetList::CreateTargetInternal( |
| debugger, user_exe_path, platform_arch, get_dependent_files, platform_sp, |
| target_sp, is_dummy_target); |
| return error; |
| } |
| |
| lldb::TargetSP TargetList::GetDummyTarget(lldb_private::Debugger &debugger) { |
| // FIXME: Maybe the dummy target should be per-Debugger |
| if (!m_dummy_target_sp || !m_dummy_target_sp->IsValid()) { |
| ArchSpec arch(Target::GetDefaultArchitecture()); |
| if (!arch.IsValid()) |
| arch = HostInfo::GetArchitecture(); |
| Status err = CreateDummyTarget( |
| debugger, arch.GetTriple().getTriple().c_str(), m_dummy_target_sp); |
| } |
| |
| return m_dummy_target_sp; |
| } |
| |
| Status TargetList::CreateDummyTarget(Debugger &debugger, |
| llvm::StringRef specified_arch_name, |
| lldb::TargetSP &target_sp) { |
| PlatformSP host_platform_sp(Platform::GetHostPlatform()); |
| return CreateTargetInternal( |
| debugger, (const char *)nullptr, specified_arch_name, false, |
| (const OptionGroupPlatform *)nullptr, target_sp, true); |
| } |
| |
| Status TargetList::CreateTargetInternal(Debugger &debugger, |
| llvm::StringRef user_exe_path, |
| const ArchSpec &specified_arch, |
| bool get_dependent_files, |
| lldb::PlatformSP &platform_sp, |
| lldb::TargetSP &target_sp, |
| bool is_dummy_target) { |
| static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); |
| Timer scoped_timer( |
| func_cat, "TargetList::CreateTarget (file = '%s', arch = '%s')", |
| user_exe_path.str().c_str(), specified_arch.GetArchitectureName()); |
| Status error; |
| |
| ArchSpec arch(specified_arch); |
| |
| if (arch.IsValid()) { |
| if (!platform_sp || |
| !platform_sp->IsCompatibleArchitecture(arch, false, nullptr)) |
| platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch); |
| } |
| |
| if (!platform_sp) |
| platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); |
| |
| if (!arch.IsValid()) |
| arch = specified_arch; |
| |
| FileSpec file(user_exe_path, false); |
| if (!file.Exists() && user_exe_path.startswith("~")) { |
| // we want to expand the tilde but we don't want to resolve any symbolic |
| // links so we can't use the FileSpec constructor's resolve flag |
| llvm::SmallString<64> unglobbed_path; |
| StandardTildeExpressionResolver Resolver; |
| Resolver.ResolveFullPath(user_exe_path, unglobbed_path); |
| |
| if (unglobbed_path.empty()) |
| file = FileSpec(user_exe_path, false); |
| else |
| file = FileSpec(unglobbed_path.c_str(), false); |
| } |
| |
| bool user_exe_path_is_bundle = false; |
| char resolved_bundle_exe_path[PATH_MAX]; |
| resolved_bundle_exe_path[0] = '\0'; |
| if (file) { |
| if (llvm::sys::fs::is_directory(file.GetPath())) |
| user_exe_path_is_bundle = true; |
| |
| if (file.IsRelative() && !user_exe_path.empty()) { |
| llvm::SmallString<64> cwd; |
| if (! llvm::sys::fs::current_path(cwd)) { |
| FileSpec cwd_file(cwd.c_str(), false); |
| cwd_file.AppendPathComponent(file); |
| if (cwd_file.Exists()) |
| file = cwd_file; |
| } |
| } |
| |
| ModuleSP exe_module_sp; |
| if (platform_sp) { |
| FileSpecList executable_search_paths( |
| Target::GetDefaultExecutableSearchPaths()); |
| ModuleSpec module_spec(file, arch); |
| error = platform_sp->ResolveExecutable(module_spec, exe_module_sp, |
| executable_search_paths.GetSize() |
| ? &executable_search_paths |
| : nullptr); |
| } |
| |
| if (error.Success() && exe_module_sp) { |
| if (exe_module_sp->GetObjectFile() == nullptr) { |
| if (arch.IsValid()) { |
| error.SetErrorStringWithFormat( |
| "\"%s\" doesn't contain architecture %s", file.GetPath().c_str(), |
| arch.GetArchitectureName()); |
| } else { |
| error.SetErrorStringWithFormat("unsupported file type \"%s\"", |
| file.GetPath().c_str()); |
| } |
| return error; |
| } |
| target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); |
| target_sp->SetExecutableModule(exe_module_sp, get_dependent_files); |
| if (user_exe_path_is_bundle) |
| exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, |
| sizeof(resolved_bundle_exe_path)); |
| } |
| } else { |
| // No file was specified, just create an empty target with any arch if a |
| // valid arch was specified |
| target_sp.reset(new Target(debugger, arch, platform_sp, is_dummy_target)); |
| } |
| |
| if (target_sp) { |
| // Set argv0 with what the user typed, unless the user specified a |
| // directory. If the user specified a directory, then it is probably a |
| // bundle that was resolved and we need to use the resolved bundle path |
| if (!user_exe_path.empty()) { |
| // Use exactly what the user typed as the first argument when we exec or |
| // posix_spawn |
| if (user_exe_path_is_bundle && resolved_bundle_exe_path[0]) { |
| target_sp->SetArg0(resolved_bundle_exe_path); |
| } else { |
| // Use resolved path |
| target_sp->SetArg0(file.GetPath().c_str()); |
| } |
| } |
| if (file.GetDirectory()) { |
| FileSpec file_dir; |
| file_dir.GetDirectory() = file.GetDirectory(); |
| target_sp->GetExecutableSearchPaths().Append(file_dir); |
| } |
| |
| // Don't put the dummy target in the target list, it's held separately. |
| if (!is_dummy_target) { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| m_selected_target_idx = m_target_list.size(); |
| m_target_list.push_back(target_sp); |
| // Now prime this from the dummy target: |
| target_sp->PrimeFromDummyTarget(debugger.GetDummyTarget()); |
| } else { |
| m_dummy_target_sp = target_sp; |
| } |
| } |
| |
| return error; |
| } |
| |
| bool TargetList::DeleteTarget(TargetSP &target_sp) { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| collection::iterator pos, end = m_target_list.end(); |
| |
| for (pos = m_target_list.begin(); pos != end; ++pos) { |
| if (pos->get() == target_sp.get()) { |
| m_target_list.erase(pos); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| TargetSP TargetList::FindTargetWithExecutableAndArchitecture( |
| const FileSpec &exe_file_spec, const ArchSpec *exe_arch_ptr) const { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| TargetSP target_sp; |
| bool full_match = (bool)exe_file_spec.GetDirectory(); |
| |
| collection::const_iterator pos, end = m_target_list.end(); |
| for (pos = m_target_list.begin(); pos != end; ++pos) { |
| Module *exe_module = (*pos)->GetExecutableModulePointer(); |
| |
| if (exe_module) { |
| if (FileSpec::Equal(exe_file_spec, exe_module->GetFileSpec(), |
| full_match)) { |
| if (exe_arch_ptr) { |
| if (!exe_arch_ptr->IsCompatibleMatch(exe_module->GetArchitecture())) |
| continue; |
| } |
| target_sp = *pos; |
| break; |
| } |
| } |
| } |
| return target_sp; |
| } |
| |
| TargetSP TargetList::FindTargetWithProcessID(lldb::pid_t pid) const { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| TargetSP target_sp; |
| collection::const_iterator pos, end = m_target_list.end(); |
| for (pos = m_target_list.begin(); pos != end; ++pos) { |
| Process *process = (*pos)->GetProcessSP().get(); |
| if (process && process->GetID() == pid) { |
| target_sp = *pos; |
| break; |
| } |
| } |
| return target_sp; |
| } |
| |
| TargetSP TargetList::FindTargetWithProcess(Process *process) const { |
| TargetSP target_sp; |
| if (process) { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| collection::const_iterator pos, end = m_target_list.end(); |
| for (pos = m_target_list.begin(); pos != end; ++pos) { |
| if (process == (*pos)->GetProcessSP().get()) { |
| target_sp = *pos; |
| break; |
| } |
| } |
| } |
| return target_sp; |
| } |
| |
| TargetSP TargetList::GetTargetSP(Target *target) const { |
| TargetSP target_sp; |
| if (target) { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| collection::const_iterator pos, end = m_target_list.end(); |
| for (pos = m_target_list.begin(); pos != end; ++pos) { |
| if (target == (*pos).get()) { |
| target_sp = *pos; |
| break; |
| } |
| } |
| } |
| return target_sp; |
| } |
| |
| uint32_t TargetList::SendAsyncInterrupt(lldb::pid_t pid) { |
| uint32_t num_async_interrupts_sent = 0; |
| |
| if (pid != LLDB_INVALID_PROCESS_ID) { |
| TargetSP target_sp(FindTargetWithProcessID(pid)); |
| if (target_sp) { |
| Process *process = target_sp->GetProcessSP().get(); |
| if (process) { |
| process->SendAsyncInterrupt(); |
| ++num_async_interrupts_sent; |
| } |
| } |
| } else { |
| // We don't have a valid pid to broadcast to, so broadcast to the target |
| // list's async broadcaster... |
| BroadcastEvent(Process::eBroadcastBitInterrupt, nullptr); |
| } |
| |
| return num_async_interrupts_sent; |
| } |
| |
| uint32_t TargetList::SignalIfRunning(lldb::pid_t pid, int signo) { |
| uint32_t num_signals_sent = 0; |
| Process *process = nullptr; |
| if (pid == LLDB_INVALID_PROCESS_ID) { |
| // Signal all processes with signal |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| collection::iterator pos, end = m_target_list.end(); |
| for (pos = m_target_list.begin(); pos != end; ++pos) { |
| process = (*pos)->GetProcessSP().get(); |
| if (process) { |
| if (process->IsAlive()) { |
| ++num_signals_sent; |
| process->Signal(signo); |
| } |
| } |
| } |
| } else { |
| // Signal a specific process with signal |
| TargetSP target_sp(FindTargetWithProcessID(pid)); |
| if (target_sp) { |
| process = target_sp->GetProcessSP().get(); |
| if (process) { |
| if (process->IsAlive()) { |
| ++num_signals_sent; |
| process->Signal(signo); |
| } |
| } |
| } |
| } |
| return num_signals_sent; |
| } |
| |
| int TargetList::GetNumTargets() const { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| return m_target_list.size(); |
| } |
| |
| lldb::TargetSP TargetList::GetTargetAtIndex(uint32_t idx) const { |
| TargetSP target_sp; |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| if (idx < m_target_list.size()) |
| target_sp = m_target_list[idx]; |
| return target_sp; |
| } |
| |
| uint32_t TargetList::GetIndexOfTarget(lldb::TargetSP target_sp) const { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| size_t num_targets = m_target_list.size(); |
| for (size_t idx = 0; idx < num_targets; idx++) { |
| if (target_sp == m_target_list[idx]) |
| return idx; |
| } |
| return UINT32_MAX; |
| } |
| |
| uint32_t TargetList::SetSelectedTarget(Target *target) { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| collection::const_iterator pos, begin = m_target_list.begin(), |
| end = m_target_list.end(); |
| for (pos = begin; pos != end; ++pos) { |
| if (pos->get() == target) { |
| m_selected_target_idx = std::distance(begin, pos); |
| return m_selected_target_idx; |
| } |
| } |
| m_selected_target_idx = 0; |
| return m_selected_target_idx; |
| } |
| |
| lldb::TargetSP TargetList::GetSelectedTarget() { |
| std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
| if (m_selected_target_idx >= m_target_list.size()) |
| m_selected_target_idx = 0; |
| return GetTargetAtIndex(m_selected_target_idx); |
| } |