214 lines
6.7 KiB
C++
214 lines
6.7 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Plugins/Platform/WebAssembly/PlatformWasm.h"
|
|
#include "Plugins/Process/wasm/ProcessWasm.h"
|
|
#include "lldb/Core/PluginManager.h"
|
|
#include "lldb/Host/FileSystem.h"
|
|
#include "lldb/Host/ProcessLaunchInfo.h"
|
|
#include "lldb/Host/common/TCPSocket.h"
|
|
#include "lldb/Interpreter/OptionValueProperties.h"
|
|
#include "lldb/Target/Process.h"
|
|
#include "lldb/Target/Target.h"
|
|
#include "lldb/Utility/LLDBLog.h"
|
|
#include "lldb/Utility/Listener.h"
|
|
#include "lldb/Utility/Log.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
LLDB_PLUGIN_DEFINE(PlatformWasm)
|
|
|
|
namespace {
|
|
#define LLDB_PROPERTIES_platformwasm
|
|
#include "PlatformWasmProperties.inc"
|
|
|
|
enum {
|
|
#define LLDB_PROPERTIES_platformwasm
|
|
#include "PlatformWasmPropertiesEnum.inc"
|
|
};
|
|
|
|
class PluginProperties : public Properties {
|
|
public:
|
|
PluginProperties() {
|
|
m_collection_sp = std::make_shared<OptionValueProperties>(
|
|
PlatformWasm::GetPluginNameStatic());
|
|
m_collection_sp->Initialize(g_platformwasm_properties);
|
|
}
|
|
|
|
FileSpec GetRuntimePath() const {
|
|
return GetPropertyAtIndexAs<FileSpec>(ePropertyRuntimePath, {});
|
|
}
|
|
|
|
Args GetRuntimeArgs() const {
|
|
Args result;
|
|
m_collection_sp->GetPropertyAtIndexAsArgs(ePropertyRuntimeArgs, result);
|
|
return result;
|
|
}
|
|
|
|
llvm::StringRef GetPortArg() const {
|
|
return GetPropertyAtIndexAs<llvm::StringRef>(ePropertyPortArg, {});
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
static PluginProperties &GetGlobalProperties() {
|
|
static PluginProperties g_settings;
|
|
return g_settings;
|
|
}
|
|
|
|
llvm::StringRef PlatformWasm::GetPluginDescriptionStatic() {
|
|
return "Platform for debugging Wasm";
|
|
}
|
|
|
|
void PlatformWasm::Initialize() {
|
|
PluginManager::RegisterPlugin(
|
|
GetPluginNameStatic(), GetPluginDescriptionStatic(),
|
|
PlatformWasm::CreateInstance, PlatformWasm::DebuggerInitialize);
|
|
}
|
|
|
|
void PlatformWasm::Terminate() {
|
|
PluginManager::UnregisterPlugin(PlatformWasm::CreateInstance);
|
|
}
|
|
|
|
void PlatformWasm::DebuggerInitialize(Debugger &debugger) {
|
|
if (!PluginManager::GetSettingForPlatformPlugin(debugger,
|
|
GetPluginNameStatic())) {
|
|
PluginManager::CreateSettingForPlatformPlugin(
|
|
debugger, GetGlobalProperties().GetValueProperties(),
|
|
"Properties for the wasm platform plugin.",
|
|
/*is_global_property=*/true);
|
|
}
|
|
}
|
|
|
|
PlatformSP PlatformWasm::CreateInstance(bool force, const ArchSpec *arch) {
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
LLDB_LOG(log, "force = {0}, arch = ({1}, {2})", force,
|
|
arch ? arch->GetArchitectureName() : "<null>",
|
|
arch ? arch->GetTriple().getTriple() : "<null>");
|
|
|
|
bool create = force;
|
|
if (!create && arch && arch->IsValid()) {
|
|
const llvm::Triple &triple = arch->GetTriple();
|
|
switch (triple.getArch()) {
|
|
case llvm::Triple::wasm32:
|
|
case llvm::Triple::wasm64:
|
|
create = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
LLDB_LOG(log, "create = {0}", create);
|
|
return create ? PlatformSP(new PlatformWasm()) : PlatformSP();
|
|
}
|
|
|
|
std::vector<ArchSpec>
|
|
PlatformWasm::GetSupportedArchitectures(const ArchSpec &process_host_arch) {
|
|
return {ArchSpec("wasm32-unknown-unknown-wasm"),
|
|
ArchSpec("wasm64-unknown-unknown-wasm")};
|
|
}
|
|
|
|
static auto get_arg_range(const Args &args) {
|
|
return llvm::make_range(args.GetArgumentArrayRef().begin(),
|
|
args.GetArgumentArrayRef().end());
|
|
}
|
|
|
|
lldb::ProcessSP PlatformWasm::DebugProcess(ProcessLaunchInfo &launch_info,
|
|
Debugger &debugger, Target &target,
|
|
Status &error) {
|
|
Log *log = GetLog(LLDBLog::Platform);
|
|
|
|
const PluginProperties &properties = GetGlobalProperties();
|
|
|
|
FileSpec runtime = properties.GetRuntimePath();
|
|
FileSystem::Instance().ResolveExecutableLocation(runtime);
|
|
|
|
if (!FileSystem::Instance().Exists(runtime)) {
|
|
error = Status::FromErrorStringWithFormatv(
|
|
"WebAssembly runtime does not exist: {0}", runtime.GetPath());
|
|
return nullptr;
|
|
}
|
|
|
|
uint16_t port = 0;
|
|
{
|
|
// Get the next available port by binding a socket to port 0.
|
|
TCPSocket listen_socket(true);
|
|
error = listen_socket.Listen("localhost:0", /*backlog=*/5);
|
|
if (error.Fail())
|
|
return nullptr;
|
|
port = listen_socket.GetLocalPortNumber();
|
|
}
|
|
|
|
if (error.Fail())
|
|
return nullptr;
|
|
|
|
Args args({runtime.GetPath(),
|
|
llvm::formatv("{0}{1}", properties.GetPortArg(), port).str()});
|
|
args.AppendArguments(properties.GetRuntimeArgs());
|
|
args.AppendArguments(launch_info.GetArguments());
|
|
|
|
launch_info.SetArguments(args, true);
|
|
launch_info.SetLaunchInSeparateProcessGroup(true);
|
|
launch_info.GetFlags().Clear(eLaunchFlagDebug);
|
|
|
|
auto exit_code = std::make_shared<std::optional<int>>();
|
|
launch_info.SetMonitorProcessCallback(
|
|
[=](lldb::pid_t pid, int signal, int status) {
|
|
LLDB_LOG(
|
|
log,
|
|
"WebAssembly runtime exited: pid = {0}, signal = {1}, status = {2}",
|
|
pid, signal, status);
|
|
exit_code->emplace(status);
|
|
});
|
|
|
|
// This is automatically done for host platform in
|
|
// Target::FinalizeFileActions, but we're not a host platform.
|
|
llvm::Error Err = launch_info.SetUpPtyRedirection();
|
|
LLDB_LOG_ERROR(log, std::move(Err), "SetUpPtyRedirection failed: {0}");
|
|
|
|
LLDB_LOG(log, "{0}", get_arg_range(launch_info.GetArguments()));
|
|
error = Host::LaunchProcess(launch_info);
|
|
if (error.Fail())
|
|
return nullptr;
|
|
|
|
ProcessSP process_sp = target.CreateProcess(
|
|
launch_info.GetListener(), wasm::ProcessWasm::GetPluginNameStatic(),
|
|
nullptr, true);
|
|
if (!process_sp) {
|
|
error = Status::FromErrorString("failed to create WebAssembly process");
|
|
return nullptr;
|
|
}
|
|
|
|
process_sp->HijackProcessEvents(launch_info.GetHijackListener());
|
|
|
|
error = process_sp->ConnectRemote(
|
|
llvm::formatv("connect://localhost:{0}", port).str());
|
|
if (error.Fail()) {
|
|
// If we know the runtime has exited, that's a better error message than
|
|
// failing to connect.
|
|
if (*exit_code)
|
|
error = Status::FromError(llvm::joinErrors(
|
|
llvm::createStringError(llvm::formatv(
|
|
"WebAssembly runtime exited with exit code {0}", **exit_code)),
|
|
error.takeError()));
|
|
|
|
return nullptr;
|
|
}
|
|
#ifndef _WIN32
|
|
if (launch_info.GetPTY().GetPrimaryFileDescriptor() !=
|
|
PseudoTerminal::invalid_fd)
|
|
process_sp->SetSTDIOFileDescriptor(
|
|
launch_info.GetPTY().ReleasePrimaryFileDescriptor());
|
|
#endif
|
|
return process_sp;
|
|
}
|