LLDB mainline
PlatformWasm.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
13#include "lldb/Core/Module.h"
19#include "lldb/Target/Process.h"
20#include "lldb/Target/Target.h"
24#include "lldb/Utility/Log.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/Support/ErrorExtras.h"
27
28using namespace lldb;
29using namespace lldb_private;
30
32
33namespace {
34#define LLDB_PROPERTIES_platformwasm
35#include "PlatformWasmProperties.inc"
36
37enum {
38#define LLDB_PROPERTIES_platformwasm
39#include "PlatformWasmPropertiesEnum.inc"
40};
41
42class PluginProperties : public Properties {
43public:
44 PluginProperties() {
45 m_collection_sp = std::make_shared<OptionValueProperties>(
47 m_collection_sp->Initialize(g_platformwasm_properties_def);
48 }
49
50 FileSpec GetRuntimePath() const {
51 return GetPropertyAtIndexAs<FileSpec>(ePropertyRuntimePath, {});
52 }
53
54 Args GetRuntimeArgs() const {
55 Args result;
56 m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyRuntimeArgs, result);
57 return result;
58 }
59
60 llvm::StringRef GetPortArg() const {
61 return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyPortArg, {});
62 }
63
64 llvm::StringRef GetEnvArg() const {
65 return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyEnvArg, {});
66 }
67};
68
69} // namespace
70
71static PluginProperties &GetGlobalProperties() {
72 static PluginProperties g_settings;
73 return g_settings;
74}
75
77 return "Platform for debugging Wasm";
78}
79
86
91
96 debugger, GetGlobalProperties().GetValueProperties(),
97 "Properties for the wasm platform plugin.",
98 /*is_global_property=*/true);
99 }
100}
101
104 LLDB_LOG(log, "force = {0}, arch = ({1}, {2})", force,
105 arch ? arch->GetArchitectureName() : "<null>",
106 arch ? arch->GetTriple().getTriple() : "<null>");
107
108 bool create = force;
109 if (!create && arch && arch->IsValid()) {
110 const llvm::Triple &triple = arch->GetTriple();
111 switch (triple.getArch()) {
112 case llvm::Triple::wasm32:
113 case llvm::Triple::wasm64:
114 create = true;
115 break;
116 default:
117 break;
118 }
119 }
120
121 LLDB_LOG(log, "create = {0}", create);
122 return create ? PlatformSP(new PlatformWasm()) : PlatformSP();
123}
124
125llvm::Expected<uint16_t> PlatformWasm::FindFreeTCPPort() {
126 TCPSocket sock(/*should_close=*/true);
127 Status status = sock.Listen("localhost:0", /*backlog=*/5);
128 if (status.Fail())
129 return status.takeError();
130 return sock.GetLocalPortNumber();
131}
132
133std::vector<ArchSpec>
135 return {ArchSpec("wasm32"), ArchSpec("wasm64")};
136}
137
139 Debugger &debugger, Target *target,
140 Status &status) {
142 return m_remote_platform_sp->Attach(attach_info, debugger, target, status);
143
145 "attaching is only supported when connected to a remote Wasm platform");
146 return nullptr;
147}
148
149Args PlatformWasm::MakeRuntimeCommand(llvm::StringRef runtime_path,
150 const Args &runtime_args,
151 llvm::StringRef port_arg, uint16_t port,
152 llvm::StringRef env_arg,
153 const Environment &env,
154 llvm::StringRef module_path,
155 const Args &inferior_args) {
156 Args args({runtime_path});
157 args.AppendArguments(runtime_args);
158 args.AppendArgument(llvm::formatv("{0}{1}", port_arg, port).str());
159
160 if (!env_arg.empty())
161 for (const auto &kv : env)
162 args.AppendArgument(
163 llvm::formatv("{0}{1}", env_arg, Environment::compose(kv)).str());
164
165 // The runtime resolves the module as a host path, while arg0 is the name the
166 // platform reports for the executable and need not resolve here.
167 Args module_args = inferior_args;
168 if (!module_path.empty()) {
169 if (module_args.GetArgumentCount() > 0)
170 module_args.ReplaceArgumentAtIndex(0, module_path);
171 else
172 module_args.AppendArgument(module_path);
173 }
174 args.AppendArguments(module_args);
175
176 return args;
177}
178
180 Debugger &debugger, Target &target,
181 Status &error) {
183 return m_remote_platform_sp->DebugProcess(launch_info, debugger, target,
184 error);
185
187
188 const PluginProperties &properties = GetGlobalProperties();
189
190 FileSpec runtime = properties.GetRuntimePath();
192
193 if (!FileSystem::Instance().Exists(runtime)) {
195 "WebAssembly runtime does not exist: {0}", runtime.GetPath());
196 return nullptr;
197 }
198
199 llvm::Expected<uint16_t> expected_port = FindFreeTCPPort();
200 if (!expected_port) {
201 error = Status::FromError(expected_port.takeError());
202 return nullptr;
203 }
204 uint16_t port = *expected_port;
205
206 std::string module_path;
207 if (ModuleSP exe_module_sp = target.GetExecutableModule())
208 module_path = exe_module_sp->GetFileSpec().GetPath();
209
211 runtime.GetPath(), properties.GetRuntimeArgs(), properties.GetPortArg(),
212 port, properties.GetEnvArg(), launch_info.GetEnvironment(), module_path,
213 launch_info.GetArguments());
214
215 launch_info.SetArguments(args, true);
216 launch_info.SetLaunchInSeparateProcessGroup(true);
217 // We're launching the Wasm runtime (a native host binary), not the target
218 // being debugged. Clear flags that don't apply to the runtime process.
219 launch_info.GetFlags().Clear(eLaunchFlagDebug | eLaunchFlagDisableASLR);
220 // The runtime itself runs with the host environment.
221 launch_info.GetEnvironment() = Host::GetEnvironment();
222
223 auto exit_code = std::make_shared<std::optional<int>>();
224 launch_info.SetMonitorProcessCallback(
225 [=](lldb::pid_t pid, int signal, int status) {
226 LLDB_LOG(
227 log,
228 "WebAssembly runtime exited: pid = {0}, signal = {1}, status = {2}",
229 pid, signal, status);
230 exit_code->emplace(status);
231 });
232
233 // This is automatically done for host platform in
234 // Target::FinalizeFileActions, but we're not a host platform.
235 llvm::Error Err = launch_info.SetUpPtyRedirection();
236 LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
237
238 LLDB_LOG(log, "{0}", GetArgRange(launch_info.GetArguments()));
239 error = Host::LaunchProcess(launch_info);
240 if (error.Fail())
241 return nullptr;
242
243 ProcessSP process_sp = target.CreateProcess(
245 nullptr, true);
246 if (!process_sp) {
247 error = Status::FromErrorString("failed to create WebAssembly process");
248 return nullptr;
249 }
250
251 process_sp->HijackProcessEvents(launch_info.GetHijackListener());
252
253 error = process_sp->ConnectRemote(
254 llvm::formatv("connect://localhost:{0}", port).str());
255 if (error.Fail()) {
256 // If we know the runtime has exited, that's a better error message than
257 // failing to connect.
258 if (*exit_code)
259 error = Status::FromError(llvm::joinErrors(
260 llvm::createStringErrorV(
261 "WebAssembly runtime exited with exit code {0}", **exit_code),
262 error.takeError()));
263
264 return nullptr;
265 }
266#ifndef _WIN32
267 if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
269 process_sp->SetSTDIOFileDescriptor(
270 launch_info.GetPTY().ReleasePrimaryFileDescriptor());
271#endif
272 return process_sp;
273}
274
276 if (IsHost())
278 "can't connect to the host platform, always connected");
279
282
283 return m_remote_platform_sp->ConnectRemote(args);
284}
static llvm::raw_ostream & error(Stream &strm)
static DynamicLoaderDarwinKernelProperties & GetGlobalProperties()
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
#define LLDB_LOG_ERROR(log, error,...)
Definition Log.h:405
static PluginProperties & GetGlobalProperties()
#define LLDB_PLUGIN_DEFINE(PluginName)
An architecture specification class.
Definition ArchSpec.h:32
bool IsValid() const
Tests if this ArchSpec is valid.
Definition ArchSpec.h:453
llvm::Triple & GetTriple()
Architecture triple accessor.
Definition ArchSpec.h:545
const char * GetArchitectureName() const
Returns a static string representing the current architecture.
Definition ArchSpec.cpp:742
A command line argument class.
Definition Args.h:33
void AppendArguments(const Args &rhs)
Definition Args.cpp:307
size_t GetArgumentCount() const
Gets the number of arguments left in this command object.
Definition Args.h:120
void ReplaceArgumentAtIndex(size_t idx, llvm::StringRef arg_str, char quote_char='\0')
Replaces the argument value at index idx to arg_str if idx is a valid argument index.
Definition Args.cpp:347
void AppendArgument(llvm::StringRef arg_str, char quote_char='\0')
Appends a new argument to the end of the list argument list.
Definition Args.cpp:332
A class to manage flag bits.
Definition Debugger.h:100
static std::string compose(const value_type &KeyValue)
Definition Environment.h:81
A file utility class.
Definition FileSpec.h:56
size_t GetPath(char *path, size_t max_path_length, bool denormalize=true) const
Extract the full path to the file.
Definition FileSpec.cpp:380
bool ResolveExecutableLocation(FileSpec &file_spec)
Call into the Host to see if it can help find the file.
static FileSystem & Instance()
ValueType Clear(ValueType mask=~static_cast< ValueType >(0))
Clear one or more flags.
Definition Flags.h:61
static Status LaunchProcess(ProcessLaunchInfo &launch_info)
Launch the process specified in launch_info.
static Environment GetEnvironment()
static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch)
static auto GetArgRange(const Args &args)
lldb::ProcessSP DebugProcess(ProcessLaunchInfo &launch_info, Debugger &debugger, Target &target, Status &error) override
Subclasses do not need to implement this function as it uses the Platform::LaunchProcess() followed b...
Status ConnectRemote(Args &args) override
static Args MakeRuntimeCommand(llvm::StringRef runtime_path, const Args &runtime_args, llvm::StringRef port_arg, uint16_t port, llvm::StringRef env_arg, const Environment &env, llvm::StringRef module_path, const Args &inferior_args)
Assemble the command line that launches the runtime on module_path, serving its GDB remote stub on po...
static void DebuggerInitialize(Debugger &debugger)
static llvm::Expected< uint16_t > FindFreeTCPPort()
Find a free TCP port by binding to port 0.
static llvm::StringRef GetPluginNameStatic()
lldb::ProcessSP Attach(ProcessAttachInfo &attach_info, Debugger &debugger, Target *target, Status &status) override
Attach to an existing process using a process ID.
std::vector< ArchSpec > GetSupportedArchitectures(const ArchSpec &process_host_arch) override
Get the platform's supported architectures in the order in which they should be searched.
static llvm::StringRef GetPluginDescriptionStatic()
bool IsHost() const
Definition Platform.h:571
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static lldb::OptionValuePropertiesSP GetSettingForPlatformPlugin(Debugger &debugger, llvm::StringRef setting_name)
static bool CreateSettingForPlatformPlugin(Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp, llvm::StringRef description, bool is_global_property)
static bool UnregisterPlugin(ABICreateInstance create_callback)
lldb::ListenerSP GetHijackListener() const
void SetArguments(const Args &args, bool first_arg_is_executable)
lldb::ListenerSP GetListener() const
Environment & GetEnvironment()
Definition ProcessInfo.h:86
void SetMonitorProcessCallback(Host::MonitorChildProcessCallback callback)
void SetLaunchInSeparateProcessGroup(bool separate)
@ invalid_fd
Invalid file descriptor value.
int GetPrimaryFileDescriptor() const
The primary file descriptor accessor.
int ReleasePrimaryFileDescriptor()
Release the primary file descriptor.
An error handling class.
Definition Status.h:118
llvm::Error takeError()
Definition Status.h:170
static Status FromErrorString(const char *str)
Definition Status.h:141
bool Fail() const
Test for error condition.
Definition Status.cpp:293
static Status static Status FromErrorStringWithFormatv(const char *format, Args &&...args)
Definition Status.h:151
static Status FromError(llvm::Error error)
Avoid using this in new code. Migrate APIs to llvm::Expected instead.
Definition Status.cpp:136
Status Listen(llvm::StringRef name, int backlog) override
uint16_t GetLocalPortNumber() const
Definition TCPSocket.cpp:86
lldb::ModuleSP GetExecutableModule()
Gets the module for the main executable.
Definition Target.cpp:1625
const lldb::ProcessSP & CreateProcess(lldb::ListenerSP listener_sp, llvm::StringRef plugin_name, const FileSpec *crash_file, bool can_connect)
Definition Target.cpp:317
static llvm::StringRef GetPluginNameStatic()
Definition ProcessWasm.h:35
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
std::shared_ptr< lldb_private::Platform > PlatformSP
std::shared_ptr< lldb_private::Process > ProcessSP
uint64_t pid_t
Definition lldb-types.h:84
std::shared_ptr< lldb_private::Module > ModuleSP