1//===-- SBPlatform.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/API/SBPlatform.h"
10#include "lldb/API/SBDebugger.h"
11#include "lldb/API/SBEnvironment.h"
12#include "lldb/API/SBError.h"
13#include "lldb/API/SBFileSpec.h"
14#include "lldb/API/SBLaunchInfo.h"
15#include "lldb/API/SBModuleSpec.h"
16#include "lldb/API/SBPlatform.h"
17#include "lldb/API/SBProcessInfoList.h"
18#include "lldb/API/SBTarget.h"
19#include "lldb/API/SBUnixSignals.h"
20#include "lldb/Host/File.h"
21#include "lldb/Target/Platform.h"
22#include "lldb/Target/Target.h"
23#include "lldb/Utility/ArchSpec.h"
24#include "lldb/Utility/Args.h"
25#include "lldb/Utility/Instrumentation.h"
26#include "lldb/Utility/Status.h"
27
28#include "llvm/Support/FileSystem.h"
29
30#include <functional>
31
32using namespace lldb;
33using namespace lldb_private;
34
35// PlatformConnectOptions
36struct PlatformConnectOptions {
37  PlatformConnectOptions(const char *url = nullptr) {
38    if (url && url[0])
39      m_url = url;
40  }
41
42  ~PlatformConnectOptions() = default;
43
44  std::string m_url;
45  std::string m_rsync_options;
46  std::string m_rsync_remote_path_prefix;
47  bool m_rsync_enabled = false;
48  bool m_rsync_omit_hostname_from_remote_path = false;
49  ConstString m_local_cache_directory;
50};
51
52// PlatformShellCommand
53struct PlatformShellCommand {
54  PlatformShellCommand(llvm::StringRef shell_interpreter,
55                       llvm::StringRef shell_command) {
56    if (!shell_interpreter.empty())
57      m_shell = shell_interpreter.str();
58
59    if (!m_shell.empty() && !shell_command.empty())
60      m_command = shell_command.str();
61  }
62
63  PlatformShellCommand(llvm::StringRef shell_command = llvm::StringRef()) {
64    if (!shell_command.empty())
65      m_command = shell_command.str();
66  }
67
68  ~PlatformShellCommand() = default;
69
70  std::string m_shell;
71  std::string m_command;
72  std::string m_working_dir;
73  std::string m_output;
74  int m_status = 0;
75  int m_signo = 0;
76  Timeout<std::ratio<1>> m_timeout = std::nullopt;
77};
78// SBPlatformConnectOptions
79SBPlatformConnectOptions::SBPlatformConnectOptions(const char *url)
80    : m_opaque_ptr(new PlatformConnectOptions(url)) {
81  LLDB_INSTRUMENT_VA(this, url);
82}
83
84SBPlatformConnectOptions::SBPlatformConnectOptions(
85    const SBPlatformConnectOptions &rhs)
86    : m_opaque_ptr(new PlatformConnectOptions()) {
87  LLDB_INSTRUMENT_VA(this, rhs);
88
89  *m_opaque_ptr = *rhs.m_opaque_ptr;
90}
91
92SBPlatformConnectOptions::~SBPlatformConnectOptions() { delete m_opaque_ptr; }
93
94SBPlatformConnectOptions &
95SBPlatformConnectOptions::operator=(const SBPlatformConnectOptions &rhs) {
96  LLDB_INSTRUMENT_VA(this, rhs);
97
98  *m_opaque_ptr = *rhs.m_opaque_ptr;
99  return *this;
100}
101
102const char *SBPlatformConnectOptions::GetURL() {
103  LLDB_INSTRUMENT_VA(this);
104
105  if (m_opaque_ptr->m_url.empty())
106    return nullptr;
107  return ConstString(m_opaque_ptr->m_url.c_str()).GetCString();
108}
109
110void SBPlatformConnectOptions::SetURL(const char *url) {
111  LLDB_INSTRUMENT_VA(this, url);
112
113  if (url && url[0])
114    m_opaque_ptr->m_url = url;
115  else
116    m_opaque_ptr->m_url.clear();
117}
118
119bool SBPlatformConnectOptions::GetRsyncEnabled() {
120  LLDB_INSTRUMENT_VA(this);
121
122  return m_opaque_ptr->m_rsync_enabled;
123}
124
125void SBPlatformConnectOptions::EnableRsync(
126    const char *options, const char *remote_path_prefix,
127    bool omit_hostname_from_remote_path) {
128  LLDB_INSTRUMENT_VA(this, options, remote_path_prefix,
129                     omit_hostname_from_remote_path);
130
131  m_opaque_ptr->m_rsync_enabled = true;
132  m_opaque_ptr->m_rsync_omit_hostname_from_remote_path =
133      omit_hostname_from_remote_path;
134  if (remote_path_prefix && remote_path_prefix[0])
135    m_opaque_ptr->m_rsync_remote_path_prefix = remote_path_prefix;
136  else
137    m_opaque_ptr->m_rsync_remote_path_prefix.clear();
138
139  if (options && options[0])
140    m_opaque_ptr->m_rsync_options = options;
141  else
142    m_opaque_ptr->m_rsync_options.clear();
143}
144
145void SBPlatformConnectOptions::DisableRsync() {
146  LLDB_INSTRUMENT_VA(this);
147
148  m_opaque_ptr->m_rsync_enabled = false;
149}
150
151const char *SBPlatformConnectOptions::GetLocalCacheDirectory() {
152  LLDB_INSTRUMENT_VA(this);
153
154  return m_opaque_ptr->m_local_cache_directory.GetCString();
155}
156
157void SBPlatformConnectOptions::SetLocalCacheDirectory(const char *path) {
158  LLDB_INSTRUMENT_VA(this, path);
159
160  if (path && path[0])
161    m_opaque_ptr->m_local_cache_directory.SetCString(path);
162  else
163    m_opaque_ptr->m_local_cache_directory = ConstString();
164}
165
166// SBPlatformShellCommand
167SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_interpreter,
168                                               const char *shell_command)
169    : m_opaque_ptr(new PlatformShellCommand(shell_interpreter, shell_command)) {
170  LLDB_INSTRUMENT_VA(this, shell_interpreter, shell_command);
171}
172
173SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command)
174    : m_opaque_ptr(new PlatformShellCommand(shell_command)) {
175  LLDB_INSTRUMENT_VA(this, shell_command);
176}
177
178SBPlatformShellCommand::SBPlatformShellCommand(
179    const SBPlatformShellCommand &rhs)
180    : m_opaque_ptr(new PlatformShellCommand()) {
181  LLDB_INSTRUMENT_VA(this, rhs);
182
183  *m_opaque_ptr = *rhs.m_opaque_ptr;
184}
185
186SBPlatformShellCommand &
187SBPlatformShellCommand::operator=(const SBPlatformShellCommand &rhs) {
188
189  LLDB_INSTRUMENT_VA(this, rhs);
190
191  *m_opaque_ptr = *rhs.m_opaque_ptr;
192  return *this;
193}
194
195SBPlatformShellCommand::~SBPlatformShellCommand() { delete m_opaque_ptr; }
196
197void SBPlatformShellCommand::Clear() {
198  LLDB_INSTRUMENT_VA(this);
199
200  m_opaque_ptr->m_output = std::string();
201  m_opaque_ptr->m_status = 0;
202  m_opaque_ptr->m_signo = 0;
203}
204
205const char *SBPlatformShellCommand::GetShell() {
206  LLDB_INSTRUMENT_VA(this);
207
208  if (m_opaque_ptr->m_shell.empty())
209    return nullptr;
210  return ConstString(m_opaque_ptr->m_shell.c_str()).GetCString();
211}
212
213void SBPlatformShellCommand::SetShell(const char *shell_interpreter) {
214  LLDB_INSTRUMENT_VA(this, shell_interpreter);
215
216  if (shell_interpreter && shell_interpreter[0])
217    m_opaque_ptr->m_shell = shell_interpreter;
218  else
219    m_opaque_ptr->m_shell.clear();
220}
221
222const char *SBPlatformShellCommand::GetCommand() {
223  LLDB_INSTRUMENT_VA(this);
224
225  if (m_opaque_ptr->m_command.empty())
226    return nullptr;
227  return ConstString(m_opaque_ptr->m_command.c_str()).GetCString();
228}
229
230void SBPlatformShellCommand::SetCommand(const char *shell_command) {
231  LLDB_INSTRUMENT_VA(this, shell_command);
232
233  if (shell_command && shell_command[0])
234    m_opaque_ptr->m_command = shell_command;
235  else
236    m_opaque_ptr->m_command.clear();
237}
238
239const char *SBPlatformShellCommand::GetWorkingDirectory() {
240  LLDB_INSTRUMENT_VA(this);
241
242  if (m_opaque_ptr->m_working_dir.empty())
243    return nullptr;
244  return ConstString(m_opaque_ptr->m_working_dir.c_str()).GetCString();
245}
246
247void SBPlatformShellCommand::SetWorkingDirectory(const char *path) {
248  LLDB_INSTRUMENT_VA(this, path);
249
250  if (path && path[0])
251    m_opaque_ptr->m_working_dir = path;
252  else
253    m_opaque_ptr->m_working_dir.clear();
254}
255
256uint32_t SBPlatformShellCommand::GetTimeoutSeconds() {
257  LLDB_INSTRUMENT_VA(this);
258
259  if (m_opaque_ptr->m_timeout)
260    return m_opaque_ptr->m_timeout->count();
261  return UINT32_MAX;
262}
263
264void SBPlatformShellCommand::SetTimeoutSeconds(uint32_t sec) {
265  LLDB_INSTRUMENT_VA(this, sec);
266
267  if (sec == UINT32_MAX)
268    m_opaque_ptr->m_timeout = std::nullopt;
269  else
270    m_opaque_ptr->m_timeout = std::chrono::seconds(sec);
271}
272
273int SBPlatformShellCommand::GetSignal() {
274  LLDB_INSTRUMENT_VA(this);
275
276  return m_opaque_ptr->m_signo;
277}
278
279int SBPlatformShellCommand::GetStatus() {
280  LLDB_INSTRUMENT_VA(this);
281
282  return m_opaque_ptr->m_status;
283}
284
285const char *SBPlatformShellCommand::GetOutput() {
286  LLDB_INSTRUMENT_VA(this);
287
288  if (m_opaque_ptr->m_output.empty())
289    return nullptr;
290  return ConstString(m_opaque_ptr->m_output.c_str()).GetCString();
291}
292
293// SBPlatform
294SBPlatform::SBPlatform() { LLDB_INSTRUMENT_VA(this); }
295
296SBPlatform::SBPlatform(const char *platform_name) {
297  LLDB_INSTRUMENT_VA(this, platform_name);
298
299  m_opaque_sp = Platform::Create(platform_name);
300}
301
302SBPlatform::SBPlatform(const SBPlatform &rhs) {
303  LLDB_INSTRUMENT_VA(this, rhs);
304
305  m_opaque_sp = rhs.m_opaque_sp;
306}
307
308SBPlatform &SBPlatform::operator=(const SBPlatform &rhs) {
309  LLDB_INSTRUMENT_VA(this, rhs);
310
311  m_opaque_sp = rhs.m_opaque_sp;
312  return *this;
313}
314
315SBPlatform::~SBPlatform() = default;
316
317SBPlatform SBPlatform::GetHostPlatform() {
318  LLDB_INSTRUMENT();
319
320  SBPlatform host_platform;
321  host_platform.m_opaque_sp = Platform::GetHostPlatform();
322  return host_platform;
323}
324
325bool SBPlatform::IsValid() const {
326  LLDB_INSTRUMENT_VA(this);
327  return this->operator bool();
328}
329SBPlatform::operator bool() const {
330  LLDB_INSTRUMENT_VA(this);
331
332  return m_opaque_sp.get() != nullptr;
333}
334
335void SBPlatform::Clear() {
336  LLDB_INSTRUMENT_VA(this);
337
338  m_opaque_sp.reset();
339}
340
341const char *SBPlatform::GetName() {
342  LLDB_INSTRUMENT_VA(this);
343
344  PlatformSP platform_sp(GetSP());
345  if (platform_sp)
346    return ConstString(platform_sp->GetName()).AsCString();
347  return nullptr;
348}
349
350lldb::PlatformSP SBPlatform::GetSP() const { return m_opaque_sp; }
351
352void SBPlatform::SetSP(const lldb::PlatformSP &platform_sp) {
353  m_opaque_sp = platform_sp;
354}
355
356const char *SBPlatform::GetWorkingDirectory() {
357  LLDB_INSTRUMENT_VA(this);
358
359  PlatformSP platform_sp(GetSP());
360  if (platform_sp)
361    return platform_sp->GetWorkingDirectory().GetPathAsConstString().AsCString();
362  return nullptr;
363}
364
365bool SBPlatform::SetWorkingDirectory(const char *path) {
366  LLDB_INSTRUMENT_VA(this, path);
367
368  PlatformSP platform_sp(GetSP());
369  if (platform_sp) {
370    if (path)
371      platform_sp->SetWorkingDirectory(FileSpec(path));
372    else
373      platform_sp->SetWorkingDirectory(FileSpec());
374    return true;
375  }
376  return false;
377}
378
379SBError SBPlatform::ConnectRemote(SBPlatformConnectOptions &connect_options) {
380  LLDB_INSTRUMENT_VA(this, connect_options);
381
382  SBError sb_error;
383  PlatformSP platform_sp(GetSP());
384  if (platform_sp && connect_options.GetURL()) {
385    Args args;
386    args.AppendArgument(connect_options.GetURL());
387    sb_error.ref() = platform_sp->ConnectRemote(args);
388  } else {
389    sb_error.SetErrorString("invalid platform");
390  }
391  return sb_error;
392}
393
394void SBPlatform::DisconnectRemote() {
395  LLDB_INSTRUMENT_VA(this);
396
397  PlatformSP platform_sp(GetSP());
398  if (platform_sp)
399    platform_sp->DisconnectRemote();
400}
401
402bool SBPlatform::IsConnected() {
403  LLDB_INSTRUMENT_VA(this);
404
405  PlatformSP platform_sp(GetSP());
406  if (platform_sp)
407    return platform_sp->IsConnected();
408  return false;
409}
410
411const char *SBPlatform::GetTriple() {
412  LLDB_INSTRUMENT_VA(this);
413
414  PlatformSP platform_sp(GetSP());
415  if (platform_sp) {
416    ArchSpec arch(platform_sp->GetSystemArchitecture());
417    if (arch.IsValid()) {
418      // Const-ify the string so we don't need to worry about the lifetime of
419      // the string
420      return ConstString(arch.GetTriple().getTriple().c_str()).GetCString();
421    }
422  }
423  return nullptr;
424}
425
426const char *SBPlatform::GetOSBuild() {
427  LLDB_INSTRUMENT_VA(this);
428
429  PlatformSP platform_sp(GetSP());
430  if (platform_sp) {
431    std::string s = platform_sp->GetOSBuildString().value_or("");
432    if (!s.empty()) {
433      // Const-ify the string so we don't need to worry about the lifetime of
434      // the string
435      return ConstString(s).GetCString();
436    }
437  }
438  return nullptr;
439}
440
441const char *SBPlatform::GetOSDescription() {
442  LLDB_INSTRUMENT_VA(this);
443
444  PlatformSP platform_sp(GetSP());
445  if (platform_sp) {
446    std::string s = platform_sp->GetOSKernelDescription().value_or("");
447    if (!s.empty()) {
448      // Const-ify the string so we don't need to worry about the lifetime of
449      // the string
450      return ConstString(s.c_str()).GetCString();
451    }
452  }
453  return nullptr;
454}
455
456const char *SBPlatform::GetHostname() {
457  LLDB_INSTRUMENT_VA(this);
458
459  PlatformSP platform_sp(GetSP());
460  if (platform_sp)
461    return ConstString(platform_sp->GetHostname()).GetCString();
462  return nullptr;
463}
464
465uint32_t SBPlatform::GetOSMajorVersion() {
466  LLDB_INSTRUMENT_VA(this);
467
468  llvm::VersionTuple version;
469  if (PlatformSP platform_sp = GetSP())
470    version = platform_sp->GetOSVersion();
471  return version.empty() ? UINT32_MAX : version.getMajor();
472}
473
474uint32_t SBPlatform::GetOSMinorVersion() {
475  LLDB_INSTRUMENT_VA(this);
476
477  llvm::VersionTuple version;
478  if (PlatformSP platform_sp = GetSP())
479    version = platform_sp->GetOSVersion();
480  return version.getMinor().value_or(UINT32_MAX);
481}
482
483uint32_t SBPlatform::GetOSUpdateVersion() {
484  LLDB_INSTRUMENT_VA(this);
485
486  llvm::VersionTuple version;
487  if (PlatformSP platform_sp = GetSP())
488    version = platform_sp->GetOSVersion();
489  return version.getSubminor().value_or(UINT32_MAX);
490}
491
492void SBPlatform::SetSDKRoot(const char *sysroot) {
493  LLDB_INSTRUMENT_VA(this, sysroot);
494  if (PlatformSP platform_sp = GetSP())
495    platform_sp->SetSDKRootDirectory(llvm::StringRef(sysroot).str());
496}
497
498SBError SBPlatform::Get(SBFileSpec &src, SBFileSpec &dst) {
499  LLDB_INSTRUMENT_VA(this, src, dst);
500
501  SBError sb_error;
502  PlatformSP platform_sp(GetSP());
503  if (platform_sp) {
504    sb_error.ref() = platform_sp->GetFile(src.ref(), dst.ref());
505  } else {
506    sb_error.SetErrorString("invalid platform");
507  }
508  return sb_error;
509}
510
511SBError SBPlatform::Put(SBFileSpec &src, SBFileSpec &dst) {
512  LLDB_INSTRUMENT_VA(this, src, dst);
513  return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
514    if (src.Exists()) {
515      uint32_t permissions = FileSystem::Instance().GetPermissions(src.ref());
516      if (permissions == 0) {
517        if (FileSystem::Instance().IsDirectory(src.ref()))
518          permissions = eFilePermissionsDirectoryDefault;
519        else
520          permissions = eFilePermissionsFileDefault;
521      }
522
523      return platform_sp->PutFile(src.ref(), dst.ref(), permissions);
524    }
525
526    Status error;
527    error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
528                                   src.ref().GetPath().c_str());
529    return error;
530  });
531}
532
533SBError SBPlatform::Install(SBFileSpec &src, SBFileSpec &dst) {
534  LLDB_INSTRUMENT_VA(this, src, dst);
535  return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
536    if (src.Exists())
537      return platform_sp->Install(src.ref(), dst.ref());
538
539    Status error;
540    error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
541                                   src.ref().GetPath().c_str());
542    return error;
543  });
544}
545
546SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
547  LLDB_INSTRUMENT_VA(this, shell_command);
548  return ExecuteConnected(
549      [&](const lldb::PlatformSP &platform_sp) {
550        const char *command = shell_command.GetCommand();
551        if (!command)
552          return Status("invalid shell command (empty)");
553
554        if (shell_command.GetWorkingDirectory() == nullptr) {
555          std::string platform_working_dir =
556              platform_sp->GetWorkingDirectory().GetPath();
557          if (!platform_working_dir.empty())
558            shell_command.SetWorkingDirectory(platform_working_dir.c_str());
559        }
560        return platform_sp->RunShellCommand(
561            shell_command.m_opaque_ptr->m_shell, command,
562            FileSpec(shell_command.GetWorkingDirectory()),
563            &shell_command.m_opaque_ptr->m_status,
564            &shell_command.m_opaque_ptr->m_signo,
565            &shell_command.m_opaque_ptr->m_output,
566            shell_command.m_opaque_ptr->m_timeout);
567      });
568}
569
570SBError SBPlatform::Launch(SBLaunchInfo &launch_info) {
571  LLDB_INSTRUMENT_VA(this, launch_info);
572  return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
573    ProcessLaunchInfo info = launch_info.ref();
574    Status error = platform_sp->LaunchProcess(info);
575    launch_info.set_ref(info);
576    return error;
577  });
578}
579
580SBProcess SBPlatform::Attach(SBAttachInfo &attach_info,
581                             const SBDebugger &debugger, SBTarget &target,
582                             SBError &error) {
583  LLDB_INSTRUMENT_VA(this, attach_info, debugger, target, error);
584
585  if (PlatformSP platform_sp = GetSP()) {
586    if (platform_sp->IsConnected()) {
587      ProcessAttachInfo &info = attach_info.ref();
588      Status status;
589      ProcessSP process_sp = platform_sp->Attach(info, debugger.ref(),
590                                                 target.GetSP().get(), status);
591      error.SetError(status);
592      return SBProcess(process_sp);
593    }
594
595    error.SetErrorString("not connected");
596    return {};
597  }
598
599  error.SetErrorString("invalid platform");
600  return {};
601}
602
603SBProcessInfoList SBPlatform::GetAllProcesses(SBError &error) {
604  if (PlatformSP platform_sp = GetSP()) {
605    if (platform_sp->IsConnected()) {
606      ProcessInstanceInfoList list = platform_sp->GetAllProcesses();
607      return SBProcessInfoList(list);
608    }
609    error.SetErrorString("not connected");
610    return {};
611  }
612
613  error.SetErrorString("invalid platform");
614  return {};
615}
616
617SBError SBPlatform::Kill(const lldb::pid_t pid) {
618  LLDB_INSTRUMENT_VA(this, pid);
619  return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
620    return platform_sp->KillProcess(pid);
621  });
622}
623
624SBError SBPlatform::ExecuteConnected(
625    const std::function<Status(const lldb::PlatformSP &)> &func) {
626  SBError sb_error;
627  const auto platform_sp(GetSP());
628  if (platform_sp) {
629    if (platform_sp->IsConnected())
630      sb_error.ref() = func(platform_sp);
631    else
632      sb_error.SetErrorString("not connected");
633  } else
634    sb_error.SetErrorString("invalid platform");
635
636  return sb_error;
637}
638
639SBError SBPlatform::MakeDirectory(const char *path, uint32_t file_permissions) {
640  LLDB_INSTRUMENT_VA(this, path, file_permissions);
641
642  SBError sb_error;
643  PlatformSP platform_sp(GetSP());
644  if (platform_sp) {
645    sb_error.ref() =
646        platform_sp->MakeDirectory(FileSpec(path), file_permissions);
647  } else {
648    sb_error.SetErrorString("invalid platform");
649  }
650  return sb_error;
651}
652
653uint32_t SBPlatform::GetFilePermissions(const char *path) {
654  LLDB_INSTRUMENT_VA(this, path);
655
656  PlatformSP platform_sp(GetSP());
657  if (platform_sp) {
658    uint32_t file_permissions = 0;
659    platform_sp->GetFilePermissions(FileSpec(path), file_permissions);
660    return file_permissions;
661  }
662  return 0;
663}
664
665SBError SBPlatform::SetFilePermissions(const char *path,
666                                       uint32_t file_permissions) {
667  LLDB_INSTRUMENT_VA(this, path, file_permissions);
668
669  SBError sb_error;
670  PlatformSP platform_sp(GetSP());
671  if (platform_sp) {
672    sb_error.ref() =
673        platform_sp->SetFilePermissions(FileSpec(path), file_permissions);
674  } else {
675    sb_error.SetErrorString("invalid platform");
676  }
677  return sb_error;
678}
679
680SBUnixSignals SBPlatform::GetUnixSignals() const {
681  LLDB_INSTRUMENT_VA(this);
682
683  if (auto platform_sp = GetSP())
684    return SBUnixSignals{platform_sp};
685
686  return SBUnixSignals();
687}
688
689SBEnvironment SBPlatform::GetEnvironment() {
690  LLDB_INSTRUMENT_VA(this);
691  PlatformSP platform_sp(GetSP());
692
693  if (platform_sp) {
694    return SBEnvironment(platform_sp->GetEnvironment());
695  }
696
697  return SBEnvironment();
698}
699
700SBError SBPlatform::SetLocateModuleCallback(
701    lldb::SBPlatformLocateModuleCallback callback, void *callback_baton) {
702  LLDB_INSTRUMENT_VA(this, callback, callback_baton);
703  PlatformSP platform_sp(GetSP());
704  if (!platform_sp)
705    return SBError("invalid platform");
706
707  if (!callback) {
708    // Clear the callback.
709    platform_sp->SetLocateModuleCallback(nullptr);
710    return SBError();
711  }
712
713  // Platform.h does not accept lldb::SBPlatformLocateModuleCallback directly
714  // because of the SBModuleSpec and SBFileSpec dependencies. Use a lambda to
715  // convert ModuleSpec/FileSpec <--> SBModuleSpec/SBFileSpec for the callback
716  // arguments.
717  platform_sp->SetLocateModuleCallback(
718      [callback, callback_baton](const ModuleSpec &module_spec,
719                                 FileSpec &module_file_spec,
720                                 FileSpec &symbol_file_spec) {
721        SBModuleSpec module_spec_sb(module_spec);
722        SBFileSpec module_file_spec_sb;
723        SBFileSpec symbol_file_spec_sb;
724
725        SBError error = callback(callback_baton, module_spec_sb,
726                                 module_file_spec_sb, symbol_file_spec_sb);
727
728        if (error.Success()) {
729          module_file_spec = module_file_spec_sb.ref();
730          symbol_file_spec = symbol_file_spec_sb.ref();
731        }
732
733        return error.ref();
734      });
735  return SBError();
736}
737