-
Notifications
You must be signed in to change notification settings - Fork 14.8k
[lldb] Add WebAssembly Process Plugin #150143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+499
−10
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
add_lldb_library(lldbPluginProcessWasm PLUGIN | ||
ProcessWasm.cpp | ||
ThreadWasm.cpp | ||
UnwindWasm.cpp | ||
|
||
LINK_LIBS | ||
lldbCore | ||
LINK_COMPONENTS | ||
Support | ||
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
//===----------------------------------------------------------------------===// | ||
DavidSpickett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// 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 "ProcessWasm.h" | ||
#include "ThreadWasm.h" | ||
#include "lldb/Core/Module.h" | ||
#include "lldb/Core/PluginManager.h" | ||
#include "lldb/Core/Value.h" | ||
#include "lldb/Utility/DataBufferHeap.h" | ||
|
||
#include "lldb/Target/UnixSignals.h" | ||
|
||
using namespace lldb; | ||
using namespace lldb_private; | ||
using namespace lldb_private::process_gdb_remote; | ||
using namespace lldb_private::wasm; | ||
|
||
LLDB_PLUGIN_DEFINE(ProcessWasm) | ||
|
||
ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp) | ||
: ProcessGDBRemote(target_sp, listener_sp) { | ||
assert(target_sp); | ||
// Wasm doesn't have any Unix-like signals as a platform concept, but pretend | ||
// like it does to appease LLDB. | ||
m_unix_signals_sp = UnixSignals::Create(target_sp->GetArchitecture()); | ||
} | ||
|
||
void ProcessWasm::Initialize() { | ||
static llvm::once_flag g_once_flag; | ||
|
||
llvm::call_once(g_once_flag, []() { | ||
PluginManager::RegisterPlugin(GetPluginNameStatic(), | ||
GetPluginDescriptionStatic(), CreateInstance, | ||
DebuggerInitialize); | ||
}); | ||
} | ||
|
||
void ProcessWasm::DebuggerInitialize(Debugger &debugger) { | ||
ProcessGDBRemote::DebuggerInitialize(debugger); | ||
} | ||
|
||
llvm::StringRef ProcessWasm::GetPluginName() { return GetPluginNameStatic(); } | ||
|
||
llvm::StringRef ProcessWasm::GetPluginNameStatic() { return "wasm"; } | ||
|
||
llvm::StringRef ProcessWasm::GetPluginDescriptionStatic() { | ||
return "GDB Remote protocol based WebAssembly debugging plug-in."; | ||
} | ||
|
||
void ProcessWasm::Terminate() { | ||
PluginManager::UnregisterPlugin(ProcessWasm::CreateInstance); | ||
} | ||
|
||
lldb::ProcessSP ProcessWasm::CreateInstance(lldb::TargetSP target_sp, | ||
ListenerSP listener_sp, | ||
const FileSpec *crash_file_path, | ||
bool can_connect) { | ||
if (crash_file_path == nullptr) | ||
return std::make_shared<ProcessWasm>(target_sp, listener_sp); | ||
return {}; | ||
} | ||
|
||
bool ProcessWasm::CanDebug(lldb::TargetSP target_sp, | ||
bool plugin_specified_by_name) { | ||
if (plugin_specified_by_name) | ||
return true; | ||
|
||
if (Module *exe_module = target_sp->GetExecutableModulePointer()) { | ||
if (ObjectFile *exe_objfile = exe_module->GetObjectFile()) | ||
return exe_objfile->GetArchitecture().GetMachine() == | ||
llvm::Triple::wasm32; | ||
DavidSpickett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// However, if there is no wasm module, we return false, otherwise, | ||
// we might use ProcessWasm to attach gdb remote. | ||
return false; | ||
} | ||
|
||
std::shared_ptr<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) { | ||
return std::make_shared<ThreadWasm>(*this, tid); | ||
} | ||
|
||
size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, | ||
Status &error) { | ||
wasm_addr_t wasm_addr(vm_addr); | ||
|
||
switch (wasm_addr.GetType()) { | ||
case WasmAddressType::Memory: | ||
case WasmAddressType::Object: | ||
return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error); | ||
case WasmAddressType::Invalid: | ||
error.FromErrorStringWithFormat( | ||
"Wasm read failed for invalid address 0x%" PRIx64, vm_addr); | ||
return 0; | ||
} | ||
} | ||
|
||
llvm::Expected<std::vector<lldb::addr_t>> | ||
ProcessWasm::GetWasmCallStack(lldb::tid_t tid) { | ||
StreamString packet; | ||
packet.Printf("qWasmCallStack:"); | ||
DavidSpickett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
packet.Printf("%llx", tid); | ||
|
||
StringExtractorGDBRemote response; | ||
if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != | ||
GDBRemoteCommunication::PacketResult::Success) | ||
return llvm::createStringError("failed to send qWasmCallStack"); | ||
|
||
if (!response.IsNormalResponse()) | ||
return llvm::createStringError("failed to get response for qWasmCallStack"); | ||
|
||
WritableDataBufferSP data_buffer_sp = | ||
std::make_shared<DataBufferHeap>(response.GetStringRef().size() / 2, 0); | ||
const size_t bytes = response.GetHexBytes(data_buffer_sp->GetData(), '\xcc'); | ||
if (bytes == 0 || bytes % sizeof(uint64_t) != 0) | ||
return llvm::createStringError("invalid response for qWasmCallStack"); | ||
|
||
// To match the Wasm specification, the addresses are encoded in little endian | ||
// byte order. | ||
DataExtractor data(data_buffer_sp, lldb::eByteOrderLittle, | ||
GetAddressByteSize()); | ||
lldb::offset_t offset = 0; | ||
std::vector<lldb::addr_t> call_stack_pcs; | ||
JDevlieghere marked this conversation as resolved.
Show resolved
Hide resolved
|
||
while (offset < bytes) | ||
call_stack_pcs.push_back(data.GetU64(&offset)); | ||
|
||
return call_stack_pcs; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H | ||
#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H | ||
|
||
#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" | ||
|
||
namespace lldb_private { | ||
namespace wasm { | ||
|
||
/// Each WebAssembly module has separated address spaces for Code and Memory. | ||
/// A WebAssembly module also has a Data section which, when the module is | ||
/// loaded, gets mapped into a region in the module Memory. | ||
enum WasmAddressType : uint8_t { Memory = 0x00, Object = 0x01, Invalid = 0xff }; | ||
|
||
/// For the purpose of debugging, we can represent all these separated 32-bit | ||
/// address spaces with a single virtual 64-bit address space. The | ||
/// wasm_addr_t provides this encoding using bitfields. | ||
struct wasm_addr_t { | ||
JDevlieghere marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint64_t offset : 32; | ||
JDevlieghere marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint64_t module_id : 30; | ||
uint64_t type : 2; | ||
|
||
wasm_addr_t(lldb::addr_t addr) | ||
: offset(addr & 0x00000000ffffffff), | ||
module_id((addr & 0x00ffffff00000000) >> 32), type(addr >> 62) {} | ||
|
||
wasm_addr_t(WasmAddressType type, uint32_t module_id, uint32_t offset) | ||
: offset(offset), module_id(module_id), type(type) {} | ||
|
||
WasmAddressType GetType() { return static_cast<WasmAddressType>(type); } | ||
|
||
operator lldb::addr_t() { return *(uint64_t *)this; } | ||
DavidSpickett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
DavidSpickett marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
static_assert(sizeof(wasm_addr_t) == 8, ""); | ||
|
||
/// ProcessWasm provides the access to the Wasm program state | ||
/// retrieved from the Wasm engine. | ||
class ProcessWasm : public process_gdb_remote::ProcessGDBRemote { | ||
public: | ||
ProcessWasm(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp); | ||
~ProcessWasm() override = default; | ||
|
||
static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp, | ||
lldb::ListenerSP listener_sp, | ||
const FileSpec *crash_file_path, | ||
JDevlieghere marked this conversation as resolved.
Show resolved
Hide resolved
|
||
bool can_connect); | ||
|
||
static void Initialize(); | ||
static void DebuggerInitialize(Debugger &debugger); | ||
static void Terminate(); | ||
|
||
static llvm::StringRef GetPluginNameStatic(); | ||
static llvm::StringRef GetPluginDescriptionStatic(); | ||
|
||
llvm::StringRef GetPluginName() override; | ||
|
||
size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, | ||
Status &error) override; | ||
|
||
bool CanDebug(lldb::TargetSP target_sp, | ||
bool plugin_specified_by_name) override; | ||
|
||
/// Retrieve the current call stack from the WebAssembly remote process. | ||
llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack(lldb::tid_t tid); | ||
|
||
protected: | ||
std::shared_ptr<process_gdb_remote::ThreadGDBRemote> | ||
CreateThread(lldb::tid_t tid) override; | ||
|
||
private: | ||
friend class UnwindWasm; | ||
process_gdb_remote::GDBRemoteDynamicRegisterInfoSP &GetRegisterInfo() { | ||
return m_register_info_sp; | ||
} | ||
|
||
ProcessWasm(const ProcessWasm &); | ||
const ProcessWasm &operator=(const ProcessWasm &) = delete; | ||
}; | ||
|
||
} // namespace wasm | ||
} // namespace lldb_private | ||
|
||
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 "ThreadWasm.h" | ||
|
||
#include "ProcessWasm.h" | ||
#include "UnwindWasm.h" | ||
#include "lldb/Target/Target.h" | ||
|
||
using namespace lldb; | ||
using namespace lldb_private; | ||
using namespace lldb_private::wasm; | ||
|
||
Unwind &ThreadWasm::GetUnwinder() { | ||
if (!m_unwinder_up) { | ||
assert(CalculateTarget()->GetArchitecture().GetMachine() == | ||
llvm::Triple::wasm32); | ||
m_unwinder_up.reset(new wasm::UnwindWasm(*this)); | ||
} | ||
return *m_unwinder_up; | ||
} | ||
|
||
llvm::Expected<std::vector<lldb::addr_t>> ThreadWasm::GetWasmCallStack() { | ||
if (ProcessSP process_sp = GetProcess()) { | ||
ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get()); | ||
return wasm_process->GetWasmCallStack(GetID()); | ||
} | ||
return llvm::createStringError("no process"); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H | ||
#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H | ||
|
||
#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h" | ||
|
||
namespace lldb_private { | ||
namespace wasm { | ||
|
||
/// ProcessWasm provides the access to the Wasm program state | ||
/// retrieved from the Wasm engine. | ||
class ThreadWasm : public process_gdb_remote::ThreadGDBRemote { | ||
public: | ||
ThreadWasm(Process &process, lldb::tid_t tid) | ||
: process_gdb_remote::ThreadGDBRemote(process, tid) {} | ||
~ThreadWasm() override = default; | ||
|
||
/// Retrieve the current call stack from the WebAssembly remote process. | ||
llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack(); | ||
|
||
protected: | ||
Unwind &GetUnwinder() override; | ||
|
||
ThreadWasm(const ThreadWasm &); | ||
const ThreadWasm &operator=(const ThreadWasm &) = delete; | ||
}; | ||
|
||
} // namespace wasm | ||
} // namespace lldb_private | ||
|
||
#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.