1 | //===-- TargetList.cpp ----------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "lldb/Target/TargetList.h" |
10 | #include "lldb/Core/Debugger.h" |
11 | #include "lldb/Core/Module.h" |
12 | #include "lldb/Core/ModuleSpec.h" |
13 | #include "lldb/Host/Host.h" |
14 | #include "lldb/Host/HostInfo.h" |
15 | #include "lldb/Interpreter/CommandInterpreter.h" |
16 | #include "lldb/Interpreter/OptionGroupPlatform.h" |
17 | #include "lldb/Symbol/ObjectFile.h" |
18 | #include "lldb/Target/Platform.h" |
19 | #include "lldb/Target/Process.h" |
20 | #include "lldb/Utility/Broadcaster.h" |
21 | #include "lldb/Utility/Event.h" |
22 | #include "lldb/Utility/State.h" |
23 | #include "lldb/Utility/TildeExpressionResolver.h" |
24 | #include "lldb/Utility/Timer.h" |
25 | |
26 | #include "llvm/ADT/SmallString.h" |
27 | #include "llvm/Support/FileSystem.h" |
28 | |
29 | using namespace lldb; |
30 | using namespace lldb_private; |
31 | |
32 | ConstString &TargetList::GetStaticBroadcasterClass() { |
33 | static ConstString class_name("lldb.targetList" ); |
34 | return class_name; |
35 | } |
36 | |
37 | // TargetList constructor |
38 | TargetList::TargetList(Debugger &debugger) |
39 | : Broadcaster(debugger.GetBroadcasterManager(), |
40 | TargetList::GetStaticBroadcasterClass().AsCString()), |
41 | m_target_list(), m_target_list_mutex(), m_selected_target_idx(0) { |
42 | CheckInWithManager(); |
43 | } |
44 | |
45 | Status TargetList::CreateTarget(Debugger &debugger, |
46 | llvm::StringRef user_exe_path, |
47 | llvm::StringRef triple_str, |
48 | LoadDependentFiles load_dependent_files, |
49 | const OptionGroupPlatform *platform_options, |
50 | TargetSP &target_sp) { |
51 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
52 | auto result = TargetList::CreateTargetInternal( |
53 | debugger, user_exe_path, triple_str, load_dependent_files, |
54 | platform_options, target_sp); |
55 | |
56 | if (target_sp && result.Success()) |
57 | AddTargetInternal(target_sp, /*do_select*/ true); |
58 | return result; |
59 | } |
60 | |
61 | Status TargetList::CreateTarget(Debugger &debugger, |
62 | llvm::StringRef user_exe_path, |
63 | const ArchSpec &specified_arch, |
64 | LoadDependentFiles load_dependent_files, |
65 | PlatformSP &platform_sp, TargetSP &target_sp) { |
66 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
67 | auto result = TargetList::CreateTargetInternal( |
68 | debugger, user_exe_path, arch: specified_arch, get_dependent_modules: load_dependent_files, |
69 | platform_sp, target_sp); |
70 | |
71 | if (target_sp && result.Success()) |
72 | AddTargetInternal(target_sp, /*do_select*/ true); |
73 | return result; |
74 | } |
75 | |
76 | Status TargetList::CreateTargetInternal( |
77 | Debugger &debugger, llvm::StringRef user_exe_path, |
78 | llvm::StringRef triple_str, LoadDependentFiles load_dependent_files, |
79 | const OptionGroupPlatform *platform_options, TargetSP &target_sp) { |
80 | Status error; |
81 | |
82 | PlatformList &platform_list = debugger.GetPlatformList(); |
83 | // Let's start by looking at the selected platform. |
84 | PlatformSP platform_sp = platform_list.GetSelectedPlatform(); |
85 | |
86 | // This variable corresponds to the architecture specified by the triple |
87 | // string. If that string was empty the currently selected platform will |
88 | // determine the architecture. |
89 | const ArchSpec arch(triple_str); |
90 | if (!triple_str.empty() && !arch.IsValid()) { |
91 | error.SetErrorStringWithFormat("invalid triple '%s'" , |
92 | triple_str.str().c_str()); |
93 | return error; |
94 | } |
95 | |
96 | ArchSpec platform_arch(arch); |
97 | |
98 | // Create a new platform if a platform was specified in the platform options |
99 | // and doesn't match the selected platform. |
100 | if (platform_options && platform_options->PlatformWasSpecified() && |
101 | !platform_options->PlatformMatches(platform_sp)) { |
102 | const bool select_platform = true; |
103 | platform_sp = platform_options->CreatePlatformWithOptions( |
104 | interpreter&: debugger.GetCommandInterpreter(), arch, make_selected: select_platform, error, |
105 | platform_arch); |
106 | if (!platform_sp) |
107 | return error; |
108 | } |
109 | |
110 | bool prefer_platform_arch = false; |
111 | auto update_platform_arch = [&](const ArchSpec &module_arch) { |
112 | // If the OS or vendor weren't specified, then adopt the module's |
113 | // architecture so that the platform matching can be more accurate. |
114 | if (!platform_arch.TripleOSWasSpecified() || |
115 | !platform_arch.TripleVendorWasSpecified()) { |
116 | prefer_platform_arch = true; |
117 | platform_arch = module_arch; |
118 | } |
119 | }; |
120 | |
121 | if (!user_exe_path.empty()) { |
122 | ModuleSpec module_spec(FileSpec(user_exe_path, FileSpec::Style::native)); |
123 | FileSystem::Instance().Resolve(file_spec&: module_spec.GetFileSpec()); |
124 | |
125 | // Try to resolve the exe based on PATH and/or platform-specific suffixes, |
126 | // but only if using the host platform. |
127 | if (platform_sp->IsHost() && |
128 | !FileSystem::Instance().Exists(file_spec: module_spec.GetFileSpec())) |
129 | FileSystem::Instance().ResolveExecutableLocation( |
130 | file_spec&: module_spec.GetFileSpec()); |
131 | |
132 | // Resolve the executable in case we are given a path to a application |
133 | // bundle like a .app bundle on MacOSX. |
134 | Host::ResolveExecutableInBundle(file&: module_spec.GetFileSpec()); |
135 | |
136 | lldb::offset_t file_offset = 0; |
137 | lldb::offset_t file_size = 0; |
138 | ModuleSpecList module_specs; |
139 | const size_t num_specs = ObjectFile::GetModuleSpecifications( |
140 | file: module_spec.GetFileSpec(), file_offset, file_size, specs&: module_specs); |
141 | |
142 | if (num_specs > 0) { |
143 | ModuleSpec matching_module_spec; |
144 | |
145 | if (num_specs == 1) { |
146 | if (module_specs.GetModuleSpecAtIndex(i: 0, module_spec&: matching_module_spec)) { |
147 | if (platform_arch.IsValid()) { |
148 | if (platform_arch.IsCompatibleMatch( |
149 | rhs: matching_module_spec.GetArchitecture())) { |
150 | // If the OS or vendor weren't specified, then adopt the module's |
151 | // architecture so that the platform matching can be more |
152 | // accurate. |
153 | update_platform_arch(matching_module_spec.GetArchitecture()); |
154 | } else { |
155 | StreamString platform_arch_strm; |
156 | StreamString module_arch_strm; |
157 | |
158 | platform_arch.DumpTriple(s&: platform_arch_strm.AsRawOstream()); |
159 | matching_module_spec.GetArchitecture().DumpTriple( |
160 | s&: module_arch_strm.AsRawOstream()); |
161 | error.SetErrorStringWithFormat( |
162 | "the specified architecture '%s' is not compatible with '%s' " |
163 | "in '%s'" , |
164 | platform_arch_strm.GetData(), module_arch_strm.GetData(), |
165 | module_spec.GetFileSpec().GetPath().c_str()); |
166 | return error; |
167 | } |
168 | } else { |
169 | // Only one arch and none was specified. |
170 | prefer_platform_arch = true; |
171 | platform_arch = matching_module_spec.GetArchitecture(); |
172 | } |
173 | } |
174 | } else if (arch.IsValid()) { |
175 | // Fat binary. A (valid) architecture was specified. |
176 | module_spec.GetArchitecture() = arch; |
177 | if (module_specs.FindMatchingModuleSpec(module_spec, |
178 | match_module_spec&: matching_module_spec)) |
179 | update_platform_arch(matching_module_spec.GetArchitecture()); |
180 | } else { |
181 | // Fat binary. No architecture specified, check if there is |
182 | // only one platform for all of the architectures. |
183 | std::vector<PlatformSP> candidates; |
184 | std::vector<ArchSpec> archs; |
185 | for (const ModuleSpec &spec : module_specs.ModuleSpecs()) |
186 | archs.push_back(x: spec.GetArchitecture()); |
187 | if (PlatformSP platform_for_archs_sp = |
188 | platform_list.GetOrCreate(archs, process_host_arch: {}, candidates)) { |
189 | platform_sp = platform_for_archs_sp; |
190 | } else if (candidates.empty()) { |
191 | error.SetErrorString("no matching platforms found for this file" ); |
192 | return error; |
193 | } else { |
194 | // More than one platform claims to support this file. |
195 | StreamString error_strm; |
196 | std::set<llvm::StringRef> platform_set; |
197 | error_strm.Printf( |
198 | format: "more than one platform supports this executable (" ); |
199 | for (const auto &candidate : candidates) { |
200 | llvm::StringRef platform_name = candidate->GetName(); |
201 | if (platform_set.count(x: platform_name)) |
202 | continue; |
203 | if (!platform_set.empty()) |
204 | error_strm.PutCString(cstr: ", " ); |
205 | error_strm.PutCString(cstr: platform_name); |
206 | platform_set.insert(x: platform_name); |
207 | } |
208 | error_strm.Printf(format: "), specify an architecture to disambiguate" ); |
209 | error.SetErrorString(error_strm.GetString()); |
210 | return error; |
211 | } |
212 | } |
213 | } |
214 | } |
215 | |
216 | // If we have a valid architecture, make sure the current platform is |
217 | // compatible with that architecture. |
218 | if (!prefer_platform_arch && arch.IsValid()) { |
219 | if (!platform_sp->IsCompatibleArchitecture( |
220 | arch, process_host_arch: {}, match: ArchSpec::CompatibleMatch, compatible_arch_ptr: nullptr)) { |
221 | platform_sp = platform_list.GetOrCreate(arch, process_host_arch: {}, platform_arch_ptr: &platform_arch); |
222 | if (platform_sp) |
223 | platform_list.SetSelectedPlatform(platform_sp); |
224 | } |
225 | } else if (platform_arch.IsValid()) { |
226 | // If "arch" isn't valid, yet "platform_arch" is, it means we have an |
227 | // executable file with a single architecture which should be used. |
228 | ArchSpec fixed_platform_arch; |
229 | if (!platform_sp->IsCompatibleArchitecture( |
230 | arch: platform_arch, process_host_arch: {}, match: ArchSpec::CompatibleMatch, compatible_arch_ptr: nullptr)) { |
231 | platform_sp = |
232 | platform_list.GetOrCreate(arch: platform_arch, process_host_arch: {}, platform_arch_ptr: &fixed_platform_arch); |
233 | if (platform_sp) |
234 | platform_list.SetSelectedPlatform(platform_sp); |
235 | } |
236 | } |
237 | |
238 | if (!platform_arch.IsValid()) |
239 | platform_arch = arch; |
240 | |
241 | return TargetList::CreateTargetInternal(debugger, user_exe_path, |
242 | arch: platform_arch, get_dependent_modules: load_dependent_files, |
243 | platform_sp, target_sp); |
244 | } |
245 | |
246 | Status TargetList::CreateTargetInternal(Debugger &debugger, |
247 | llvm::StringRef user_exe_path, |
248 | const ArchSpec &specified_arch, |
249 | LoadDependentFiles load_dependent_files, |
250 | lldb::PlatformSP &platform_sp, |
251 | lldb::TargetSP &target_sp) { |
252 | LLDB_SCOPED_TIMERF("TargetList::CreateTarget (file = '%s', arch = '%s')" , |
253 | user_exe_path.str().c_str(), |
254 | specified_arch.GetArchitectureName()); |
255 | Status error; |
256 | const bool is_dummy_target = false; |
257 | |
258 | ArchSpec arch(specified_arch); |
259 | |
260 | if (arch.IsValid()) { |
261 | if (!platform_sp || !platform_sp->IsCompatibleArchitecture( |
262 | arch, process_host_arch: {}, match: ArchSpec::CompatibleMatch, compatible_arch_ptr: nullptr)) |
263 | platform_sp = |
264 | debugger.GetPlatformList().GetOrCreate(arch: specified_arch, process_host_arch: {}, platform_arch_ptr: &arch); |
265 | } |
266 | |
267 | if (!platform_sp) |
268 | platform_sp = debugger.GetPlatformList().GetSelectedPlatform(); |
269 | |
270 | if (!arch.IsValid()) |
271 | arch = specified_arch; |
272 | |
273 | FileSpec file(user_exe_path); |
274 | if (!FileSystem::Instance().Exists(file_spec: file) && user_exe_path.starts_with(Prefix: "~" )) { |
275 | // we want to expand the tilde but we don't want to resolve any symbolic |
276 | // links so we can't use the FileSpec constructor's resolve flag |
277 | llvm::SmallString<64> unglobbed_path; |
278 | StandardTildeExpressionResolver Resolver; |
279 | Resolver.ResolveFullPath(Expr: user_exe_path, Output&: unglobbed_path); |
280 | |
281 | if (unglobbed_path.empty()) |
282 | file = FileSpec(user_exe_path); |
283 | else |
284 | file = FileSpec(unglobbed_path.c_str()); |
285 | } |
286 | |
287 | bool user_exe_path_is_bundle = false; |
288 | char resolved_bundle_exe_path[PATH_MAX]; |
289 | resolved_bundle_exe_path[0] = '\0'; |
290 | if (file) { |
291 | if (FileSystem::Instance().IsDirectory(file_spec: file)) |
292 | user_exe_path_is_bundle = true; |
293 | |
294 | if (file.IsRelative() && !user_exe_path.empty()) { |
295 | llvm::SmallString<64> cwd; |
296 | if (! llvm::sys::fs::current_path(result&: cwd)) { |
297 | FileSpec cwd_file(cwd.c_str()); |
298 | cwd_file.AppendPathComponent(new_path: file); |
299 | if (FileSystem::Instance().Exists(file_spec: cwd_file)) |
300 | file = cwd_file; |
301 | } |
302 | } |
303 | |
304 | ModuleSP exe_module_sp; |
305 | if (platform_sp) { |
306 | FileSpecList executable_search_paths( |
307 | Target::GetDefaultExecutableSearchPaths()); |
308 | ModuleSpec module_spec(file, arch); |
309 | error = platform_sp->ResolveExecutable(module_spec, module_sp&: exe_module_sp, |
310 | module_search_paths_ptr: executable_search_paths.GetSize() |
311 | ? &executable_search_paths |
312 | : nullptr); |
313 | } |
314 | |
315 | if (error.Success() && exe_module_sp) { |
316 | if (exe_module_sp->GetObjectFile() == nullptr) { |
317 | if (arch.IsValid()) { |
318 | error.SetErrorStringWithFormat( |
319 | "\"%s\" doesn't contain architecture %s" , file.GetPath().c_str(), |
320 | arch.GetArchitectureName()); |
321 | } else { |
322 | error.SetErrorStringWithFormat("unsupported file type \"%s\"" , |
323 | file.GetPath().c_str()); |
324 | } |
325 | return error; |
326 | } |
327 | target_sp.reset(p: new Target(debugger, arch, platform_sp, is_dummy_target)); |
328 | debugger.GetTargetList().RegisterInProcessTarget(target_sp); |
329 | target_sp->SetExecutableModule(module_sp&: exe_module_sp, load_dependent_files); |
330 | if (user_exe_path_is_bundle) |
331 | exe_module_sp->GetFileSpec().GetPath(path: resolved_bundle_exe_path, |
332 | max_path_length: sizeof(resolved_bundle_exe_path)); |
333 | if (target_sp->GetPreloadSymbols()) |
334 | exe_module_sp->PreloadSymbols(); |
335 | } |
336 | } else { |
337 | // No file was specified, just create an empty target with any arch if a |
338 | // valid arch was specified |
339 | target_sp.reset(p: new Target(debugger, arch, platform_sp, is_dummy_target)); |
340 | debugger.GetTargetList().RegisterInProcessTarget(target_sp); |
341 | } |
342 | |
343 | if (!target_sp) |
344 | return error; |
345 | |
346 | // Set argv0 with what the user typed, unless the user specified a |
347 | // directory. If the user specified a directory, then it is probably a |
348 | // bundle that was resolved and we need to use the resolved bundle path |
349 | if (!user_exe_path.empty()) { |
350 | // Use exactly what the user typed as the first argument when we exec or |
351 | // posix_spawn |
352 | if (user_exe_path_is_bundle && resolved_bundle_exe_path[0]) { |
353 | target_sp->SetArg0(resolved_bundle_exe_path); |
354 | } else { |
355 | // Use resolved path |
356 | target_sp->SetArg0(file.GetPath().c_str()); |
357 | } |
358 | } |
359 | if (file.GetDirectory()) { |
360 | FileSpec file_dir; |
361 | file_dir.SetDirectory(file.GetDirectory()); |
362 | target_sp->AppendExecutableSearchPaths(file_dir); |
363 | } |
364 | |
365 | // Now prime this from the dummy target: |
366 | target_sp->PrimeFromDummyTarget(target&: debugger.GetDummyTarget()); |
367 | |
368 | return error; |
369 | } |
370 | |
371 | bool TargetList::DeleteTarget(TargetSP &target_sp) { |
372 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
373 | auto it = llvm::find(Range&: m_target_list, Val: target_sp); |
374 | if (it == m_target_list.end()) |
375 | return false; |
376 | |
377 | m_target_list.erase(position: it); |
378 | return true; |
379 | } |
380 | |
381 | TargetSP TargetList::FindTargetWithExecutableAndArchitecture( |
382 | const FileSpec &exe_file_spec, const ArchSpec *exe_arch_ptr) const { |
383 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
384 | auto it = std::find_if(first: m_target_list.begin(), last: m_target_list.end(), |
385 | pred: [&exe_file_spec, exe_arch_ptr](const TargetSP &item) { |
386 | Module *exe_module = item->GetExecutableModulePointer(); |
387 | if (!exe_module || |
388 | !FileSpec::Match(pattern: exe_file_spec, file: exe_module->GetFileSpec())) |
389 | return false; |
390 | |
391 | return !exe_arch_ptr || |
392 | exe_arch_ptr->IsCompatibleMatch(rhs: exe_module->GetArchitecture()); |
393 | }); |
394 | |
395 | if (it != m_target_list.end()) |
396 | return *it; |
397 | |
398 | return TargetSP(); |
399 | } |
400 | |
401 | TargetSP TargetList::FindTargetWithProcessID(lldb::pid_t pid) const { |
402 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
403 | auto it = std::find_if(first: m_target_list.begin(), last: m_target_list.end(), |
404 | pred: [pid](const TargetSP &item) { |
405 | auto *process_ptr = item->GetProcessSP().get(); |
406 | return process_ptr && (process_ptr->GetID() == pid); |
407 | }); |
408 | |
409 | if (it != m_target_list.end()) |
410 | return *it; |
411 | |
412 | return TargetSP(); |
413 | } |
414 | |
415 | TargetSP TargetList::FindTargetWithProcess(Process *process) const { |
416 | TargetSP target_sp; |
417 | if (!process) |
418 | return target_sp; |
419 | |
420 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
421 | auto it = std::find_if(first: m_target_list.begin(), last: m_target_list.end(), |
422 | pred: [process](const TargetSP &item) { |
423 | return item->GetProcessSP().get() == process; |
424 | }); |
425 | |
426 | if (it != m_target_list.end()) |
427 | target_sp = *it; |
428 | |
429 | return target_sp; |
430 | } |
431 | |
432 | TargetSP TargetList::GetTargetSP(Target *target) const { |
433 | TargetSP target_sp; |
434 | if (!target) |
435 | return target_sp; |
436 | |
437 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
438 | auto it = std::find_if(first: m_target_list.begin(), last: m_target_list.end(), |
439 | pred: [target](const TargetSP &item) { return item.get() == target; }); |
440 | if (it != m_target_list.end()) |
441 | target_sp = *it; |
442 | |
443 | return target_sp; |
444 | } |
445 | |
446 | uint32_t TargetList::SendAsyncInterrupt(lldb::pid_t pid) { |
447 | uint32_t num_async_interrupts_sent = 0; |
448 | |
449 | if (pid != LLDB_INVALID_PROCESS_ID) { |
450 | TargetSP target_sp(FindTargetWithProcessID(pid)); |
451 | if (target_sp) { |
452 | Process *process = target_sp->GetProcessSP().get(); |
453 | if (process) { |
454 | process->SendAsyncInterrupt(); |
455 | ++num_async_interrupts_sent; |
456 | } |
457 | } |
458 | } else { |
459 | // We don't have a valid pid to broadcast to, so broadcast to the target |
460 | // list's async broadcaster... |
461 | BroadcastEvent(event_type: Process::eBroadcastBitInterrupt, event_data_sp: nullptr); |
462 | } |
463 | |
464 | return num_async_interrupts_sent; |
465 | } |
466 | |
467 | uint32_t TargetList::SignalIfRunning(lldb::pid_t pid, int signo) { |
468 | uint32_t num_signals_sent = 0; |
469 | Process *process = nullptr; |
470 | if (pid == LLDB_INVALID_PROCESS_ID) { |
471 | // Signal all processes with signal |
472 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
473 | for (const auto &target_sp : m_target_list) { |
474 | process = target_sp->GetProcessSP().get(); |
475 | if (process && process->IsAlive()) { |
476 | ++num_signals_sent; |
477 | process->Signal(signal: signo); |
478 | } |
479 | } |
480 | } else { |
481 | // Signal a specific process with signal |
482 | TargetSP target_sp(FindTargetWithProcessID(pid)); |
483 | if (target_sp) { |
484 | process = target_sp->GetProcessSP().get(); |
485 | if (process && process->IsAlive()) { |
486 | ++num_signals_sent; |
487 | process->Signal(signal: signo); |
488 | } |
489 | } |
490 | } |
491 | return num_signals_sent; |
492 | } |
493 | |
494 | size_t TargetList::GetNumTargets() const { |
495 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
496 | return m_target_list.size(); |
497 | } |
498 | |
499 | lldb::TargetSP TargetList::GetTargetAtIndex(uint32_t idx) const { |
500 | TargetSP target_sp; |
501 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
502 | if (idx < m_target_list.size()) |
503 | target_sp = m_target_list[idx]; |
504 | return target_sp; |
505 | } |
506 | |
507 | uint32_t TargetList::GetIndexOfTarget(lldb::TargetSP target_sp) const { |
508 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
509 | auto it = llvm::find(Range: m_target_list, Val: target_sp); |
510 | if (it != m_target_list.end()) |
511 | return std::distance(first: m_target_list.begin(), last: it); |
512 | return UINT32_MAX; |
513 | } |
514 | |
515 | void TargetList::AddTargetInternal(TargetSP target_sp, bool do_select) { |
516 | lldbassert(!llvm::is_contained(m_target_list, target_sp) && |
517 | "target already exists it the list" ); |
518 | UnregisterInProcessTarget(target_sp); |
519 | m_target_list.push_back(x: std::move(target_sp)); |
520 | if (do_select) |
521 | SetSelectedTargetInternal(m_target_list.size() - 1); |
522 | } |
523 | |
524 | void TargetList::SetSelectedTargetInternal(uint32_t index) { |
525 | lldbassert(!m_target_list.empty()); |
526 | m_selected_target_idx = index < m_target_list.size() ? index : 0; |
527 | } |
528 | |
529 | void TargetList::SetSelectedTarget(uint32_t index) { |
530 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
531 | SetSelectedTargetInternal(index); |
532 | } |
533 | |
534 | void TargetList::SetSelectedTarget(const TargetSP &target_sp) { |
535 | // Don't allow an invalid target shared pointer or a target that has been |
536 | // destroyed to become the selected target. |
537 | if (target_sp && target_sp->IsValid()) { |
538 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
539 | auto it = llvm::find(Range&: m_target_list, Val: target_sp); |
540 | SetSelectedTargetInternal(std::distance(first: m_target_list.begin(), last: it)); |
541 | } |
542 | } |
543 | |
544 | lldb::TargetSP TargetList::GetSelectedTarget() { |
545 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
546 | if (m_selected_target_idx >= m_target_list.size()) |
547 | m_selected_target_idx = 0; |
548 | return GetTargetAtIndex(idx: m_selected_target_idx); |
549 | } |
550 | |
551 | bool TargetList::AnyTargetContainsModule(Module &module) { |
552 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
553 | for (const auto &target_sp : m_target_list) { |
554 | if (target_sp->GetImages().FindModule(module_ptr: &module)) |
555 | return true; |
556 | } |
557 | for (const auto &target_sp: m_in_process_target_list) { |
558 | if (target_sp->GetImages().FindModule(module_ptr: &module)) |
559 | return true; |
560 | } |
561 | return false; |
562 | } |
563 | |
564 | void TargetList::RegisterInProcessTarget(TargetSP target_sp) { |
565 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
566 | [[maybe_unused]] bool was_added; |
567 | std::tie(args: std::ignore, args&: was_added) = |
568 | m_in_process_target_list.insert(x: target_sp); |
569 | assert(was_added && "Target pointer was left in the in-process map" ); |
570 | } |
571 | |
572 | void TargetList::UnregisterInProcessTarget(TargetSP target_sp) { |
573 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
574 | [[maybe_unused]] bool was_present = |
575 | m_in_process_target_list.erase(x: target_sp); |
576 | assert(was_present && "Target pointer being removed was not registered" ); |
577 | } |
578 | |
579 | bool TargetList::IsTargetInProcess(TargetSP target_sp) { |
580 | std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex); |
581 | return m_in_process_target_list.count(x: target_sp) == 1; |
582 | } |
583 | |