From 3fda41c47437d10bd857fd2fe796913da994b6c6 Mon Sep 17 00:00:00 2001 From: Jeffrey Tan Date: Mon, 2 Jun 2025 12:04:50 -0700 Subject: [PATCH 1/3] AMD GPU plugin v0 --- .../Process/gdb-remote/LLDBServerPlugin.cpp | 4 +- .../Process/gdb-remote/LLDBServerPlugin.h | 4 +- lldb/tools/lldb-server/CMakeLists.txt | 9 +- .../lldb-server/Plugins/AMDGPU/CMakeLists.txt | 30 + .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp | 598 ++++++++++++++++++ .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.h | 147 +++++ .../Plugins/AMDGPU/ProcessAMDGPU.cpp | 557 ++++++++++++++++ .../Plugins/AMDGPU/ProcessAMDGPU.h | 146 +++++ .../Plugins/AMDGPU/RegisterContextAMDGPU.cpp | 517 +++++++++++++++ .../Plugins/AMDGPU/RegisterContextAMDGPU.h | 65 ++ .../Plugins/AMDGPU/ThreadAMDGPU.cpp | 63 ++ .../lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h | 75 +++ lldb/tools/lldb-server/Plugins/CMakeLists.txt | 6 +- .../MockGPU/LLDBServerPluginMockGPU.cpp | 4 +- .../Plugins/MockGPU/LLDBServerPluginMockGPU.h | 2 +- lldb/tools/lldb-server/lldb-gdbserver.cpp | 18 +- llvm/cmake/modules/CrossCompile.cmake | 1 + 17 files changed, 2232 insertions(+), 14 deletions(-) create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp create mode 100644 lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp index a520309f4e916..439f6b6af249c 100644 --- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.cpp @@ -15,8 +15,8 @@ using namespace lldb_private; using namespace lldb_server; -LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process) : - m_native_process(native_process) {} +LLDBServerPlugin::LLDBServerPlugin(GDBServer &native_process, MainLoop &main_loop) : + m_native_process(native_process), m_main_loop(main_loop) {} LLDBServerPlugin::~LLDBServerPlugin() {} diff --git a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h index 82f449c5c088c..a5c98ce26c177 100644 --- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h +++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerPlugin.h @@ -35,14 +35,14 @@ class LLDBServerPlugin { using GDBServer = process_gdb_remote::GDBRemoteCommunicationServerLLGS; using Manager = NativeProcessProtocol::Manager; GDBServer &m_native_process; - MainLoop m_main_loop; + MainLoop &m_main_loop; std::unique_ptr m_process_manager_up; std::unique_ptr m_gdb_server; bool m_is_listening = false; bool m_is_connected = false; public: - LLDBServerPlugin(GDBServer &native_process); + LLDBServerPlugin(GDBServer &native_process, MainLoop &main_loop); virtual ~LLDBServerPlugin(); /// Check if we are already connected. diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt index b842e8fd8632e..4126c02829441 100644 --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -42,6 +42,14 @@ if(APPLE_EMBEDDED) endif() endif() +if(DEFINED ROCM_PATH AND EXISTS ${ROCM_PATH}) + add_definitions(-DLLDB_ENABLE_AMDGPU_PLUGIN=1) + list(APPEND LLDB_PLUGINS lldbServerPluginAMDGPU) +else() + add_definitions(-DLLDB_ENABLE_MOCKGPU_PLUGIN=1) + list(APPEND LLDB_PLUGINS lldbServerPluginMockGPU) +endif() + add_lldb_tool(lldb-server lldb-gdbserver.cpp lldb-platform.cpp @@ -60,7 +68,6 @@ add_lldb_tool(lldb-server lldbPluginInstructionMIPS64 lldbPluginInstructionRISCV ${LLDB_SYSTEM_LIBS} - lldbServerPluginMockGPU LINK_COMPONENTS Option diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt new file mode 100644 index 0000000000000..f19d4a72aca9b --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt @@ -0,0 +1,30 @@ + +# Set ROCM paths +set(ROCM_PATH "" CACHE PATH "Path to ROCm installation") + +message(STATUS "ROCM_PATH is set to: '${ROCM_PATH}'") +if(NOT ROCM_PATH OR ROCM_PATH STREQUAL "") + message(FATAL_ERROR "ROCM_PATH must be specified. Use -DROCM_PATH=/path/to/rocm") +endif() + +if (NOT EXISTS ${ROCM_PATH}) + message(FATAL_ERROR "ROCM_PATH does not exist: '${ROCM_PATH}'") +endif() + +# Find AMD-dbgapi package using the provided CMake config +set(amd-dbgapi_DIR "${ROCM_PATH}/lib/cmake/amd-dbgapi" CACHE PATH "Path to amd-dbgapi cmake config") +find_package(amd-dbgapi REQUIRED CONFIG) + +include_directories(${ROCM_PATH}/include) + +add_lldb_library(lldbServerPluginAMDGPU + LLDBServerPluginAMDGPU.cpp + ProcessAMDGPU.cpp + RegisterContextAMDGPU.cpp + ThreadAMDGPU.cpp + + LINK_LIBS + amd-dbgapi +) +set_target_properties(lldbServerPluginAMDGPU PROPERTIES LINK_FLAGS "-Wl,-rpath,${ROCM_PATH}/lib") +target_include_directories(lldbServerPluginAMDGPU PRIVATE "${LLDB_SOURCE_DIR}/source" ${ROCM_PATH}/include "../..") diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp new file mode 100644 index 0000000000000..32473b397baba --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp @@ -0,0 +1,598 @@ +//===-- LLDBServerPluginAMDGPU.cpp -----------------------------*- 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 "LLDBServerPluginAMDGPU.h" +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include "ProcessAMDGPU.h" +#include "ThreadAMDGPU.h" +#include "lldb/Host/common/TCPSocket.h" +#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" +#include "llvm/Support/Error.h" + +#include +#include +#include +#include +#include +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace lldb_private::process_gdb_remote; + +static amd_dbgapi_status_t amd_dbgapi_client_process_get_info_callback( + amd_dbgapi_client_process_id_t client_process_id, + amd_dbgapi_client_process_info_t query, size_t value_size, void *value) { + LLDBServerPluginAMDGPU *debugger = + reinterpret_cast(client_process_id); + lldb::pid_t pid = debugger->GetNativeProcess()->GetID(); + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "amd_dbgapi_client_process_get_info_callback callback, with query " + "%d, pid %d", + query, pid); + switch (query) { + case AMD_DBGAPI_CLIENT_PROCESS_INFO_OS_PID: { + if (value_size != sizeof(amd_dbgapi_os_process_id_t)) + return AMD_DBGAPI_STATUS_ERROR_INVALID_ARGUMENT_COMPATIBILITY; + *static_cast(value) = pid; + return AMD_DBGAPI_STATUS_SUCCESS; + } + case AMD_DBGAPI_CLIENT_PROCESS_INFO_CORE_STATE: { + return AMD_DBGAPI_STATUS_SUCCESS; + } + } + return AMD_DBGAPI_STATUS_SUCCESS; +} + +static amd_dbgapi_status_t amd_dbgapi_insert_breakpoint_callback( + amd_dbgapi_client_process_id_t client_process_id, + amd_dbgapi_global_address_t address, + amd_dbgapi_breakpoint_id_t breakpoint_id) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "insert_breakpoint callback at address: 0x%llx", address); + LLDBServerPluginAMDGPU *debugger = + reinterpret_cast(client_process_id); + debugger->GetNativeProcess()->Halt(); + LLDB_LOGF(GetLog(GDBRLog::Plugin), "insert_breakpoint callback success"); + LLDBServerPluginAMDGPU::GPUInternalBreakpoinInfo bp_info; + bp_info.addr = address; + bp_info.breakpoind_id = breakpoint_id; + debugger->m_gpu_internal_bp.emplace(std::move(bp_info)); + debugger->m_wait_for_gpu_internal_bp_stop = true; + return AMD_DBGAPI_STATUS_SUCCESS; +} + +/* remove_breakpoint callback. */ + +static amd_dbgapi_status_t amd_dbgapi_remove_breakpoint_callback( + amd_dbgapi_client_process_id_t client_process_id, + amd_dbgapi_breakpoint_id_t breakpoint_id) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "remove_breakpoint callback for %llu", + breakpoint_id.handle); + return AMD_DBGAPI_STATUS_SUCCESS; +} + +/* xfer_global_memory callback. */ + +static amd_dbgapi_status_t amd_dbgapi_xfer_global_memory_callback( + amd_dbgapi_client_process_id_t client_process_id, + amd_dbgapi_global_address_t global_address, amd_dbgapi_size_t *value_size, + void *read_buffer, const void *write_buffer) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "xfer_global_memory callback"); + + return AMD_DBGAPI_STATUS_SUCCESS; +} + +static void amd_dbgapi_log_message_callback(amd_dbgapi_log_level_t level, + const char *message) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "ROCdbgapi [%d]: %s", level, message); +}; + +static amd_dbgapi_callbacks_t s_dbgapi_callbacks = { + .allocate_memory = malloc, + .deallocate_memory = free, + .client_process_get_info = amd_dbgapi_client_process_get_info_callback, + .insert_breakpoint = amd_dbgapi_insert_breakpoint_callback, + .remove_breakpoint = amd_dbgapi_remove_breakpoint_callback, + .xfer_global_memory = amd_dbgapi_xfer_global_memory_callback, + .log_message = amd_dbgapi_log_message_callback, +}; + +LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU( + LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop) + : LLDBServerPlugin(native_process, main_loop) { + m_process_manager_up.reset(new ProcessManagerAMDGPU(main_loop)); + m_gdb_server.reset( + new GDBRemoteCommunicationServerLLGS(m_main_loop, *m_process_manager_up)); +} + +LLDBServerPluginAMDGPU::~LLDBServerPluginAMDGPU() { CloseFDs(); } + +llvm::StringRef LLDBServerPluginAMDGPU::GetPluginName() { return "amd-gpu"; } + +void LLDBServerPluginAMDGPU::CloseFDs() { + if (m_fds[0] != -1) { + close(m_fds[0]); + m_fds[0] = -1; + } + if (m_fds[1] != -1) { + close(m_fds[1]); + m_fds[1] = -1; + } +} + +int LLDBServerPluginAMDGPU::GetEventFileDescriptorAtIndex(size_t idx) { + if (idx != 0) + return -1; + if (m_fds[0] == -1) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, m_fds) == -1) { + m_fds[0] = -1; + m_fds[1] = -1; + } + } + return m_fds[0]; +} + +bool LLDBServerPluginAMDGPU::initRocm() { + // Initialize AMD Debug API with callbacks + amd_dbgapi_status_t status = amd_dbgapi_initialize(&s_dbgapi_callbacks); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Failed to initialize AMD debug API"); + exit(-1); + } + + // Attach to the process with AMD Debug API + status = amd_dbgapi_process_attach( + reinterpret_cast( + this), // Use pid_ as client_process_id + &m_gpu_pid); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Failed to attach to process with AMD debug API: %d", status); + amd_dbgapi_finalize(); + return false; + } + + // Get the process notifier + status = + amd_dbgapi_process_get_info(m_gpu_pid, AMD_DBGAPI_PROCESS_INFO_NOTIFIER, + sizeof(m_notifier_fd), &m_notifier_fd); + + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Failed to get process notifier: %d", + status); + amd_dbgapi_process_detach(m_gpu_pid); + amd_dbgapi_finalize(); + return false; + } + + // event_handler_.addNotifierFd(m_notifier_fd); + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Process notifier fd: %d", m_notifier_fd); + + amd_dbgapi_event_id_t eventId; + amd_dbgapi_event_kind_t eventKind; + // Process all pending events + if (amd_dbgapi_process_next_pending_event(m_gpu_pid, &eventId, &eventKind) == + AMD_DBGAPI_STATUS_SUCCESS) { + GetGPUProcess()->handleDebugEvent(eventId, eventKind); + + // Mark event as processed + amd_dbgapi_event_processed(eventId); + } + + amd_dbgapi_architecture_id_t architecture_id; + // TODO: do not hardcode the device id + status = amd_dbgapi_get_architecture(0x04C, &architecture_id); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + // Handle error + LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_get_architecture failed"); + return false; + } + m_architecture_id = architecture_id; + + return true; +} + +bool LLDBServerPluginAMDGPU::processGPUEvent() { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "processGPUEvent"); + char buf[256]; + ssize_t bytesRead = 0; + bool result = false; + do { + do { + bytesRead = read(m_notifier_fd, buf, sizeof(buf)); + } while (bytesRead <= 0); + + auto *process = GetGPUProcess(); + process->m_wave_ids.clear(); + amd_dbgapi_status_t status = amd_dbgapi_process_set_progress( + m_gpu_pid, AMD_DBGAPI_PROGRESS_NO_FORWARD); + assert(status == AMD_DBGAPI_STATUS_SUCCESS); + process_event_queue(AMD_DBGAPI_EVENT_KIND_NONE); + if (process->m_gpu_state == ProcessAMDGPU::State::GPUStopped) { + for (auto wave_id : process->m_wave_ids) { + process->AddThread(wave_id); + } + process->Halt(); + } + status = + amd_dbgapi_process_set_progress(m_gpu_pid, AMD_DBGAPI_PROGRESS_NORMAL); + assert(status == AMD_DBGAPI_STATUS_SUCCESS); + break; + } while (true); + return result; +} + +bool LLDBServerPluginAMDGPU::HandleEventFileDescriptorEvent(int fd) { + return processGPUEvent(); +} + +void LLDBServerPluginAMDGPU::AcceptAndMainLoopThread( + std::unique_ptr listen_socket_up) { + Log *log = GetLog(GDBRLog::Plugin); + LLDB_LOGF(log, "%s spawned", __PRETTY_FUNCTION__); + Socket *socket = nullptr; + Status error = listen_socket_up->Accept(std::chrono::seconds(30), socket); + // Scope for lock guard. + { + // Protect access to m_is_listening and m_is_connected. + std::lock_guard guard(m_connect_mutex); + m_is_listening = false; + if (error.Fail()) { + LLDB_LOGF(log, "%s error returned from Accept(): %s", __PRETTY_FUNCTION__, + error.AsCString()); + return; + } + m_is_connected = true; + } + + LLDB_LOGF(log, "%s initializing connection", __PRETTY_FUNCTION__); + std::unique_ptr connection_up( + new ConnectionFileDescriptor(socket)); + m_gdb_server->InitializeConnection(std::move(connection_up)); + LLDB_LOGF(log, "%s running main loop", __PRETTY_FUNCTION__); + m_main_loop_status = m_main_loop.Run(); + LLDB_LOGF(log, "%s main loop exited!", __PRETTY_FUNCTION__); + if (m_main_loop_status.Fail()) { + LLDB_LOGF(log, "%s main loop exited with an error: %s", __PRETTY_FUNCTION__, + m_main_loop_status.AsCString()); + } + // Protect access to m_is_connected. + std::lock_guard guard(m_connect_mutex); + m_is_connected = false; +} + +std::optional +LLDBServerPluginAMDGPU::CreateConnection() { + std::lock_guard guard(m_connect_mutex); + Log *log = GetLog(GDBRLog::Plugin); + LLDB_LOGF(log, "%s called", __PRETTY_FUNCTION__); + if (m_is_connected) { + LLDB_LOGF(log, "%s already connected", __PRETTY_FUNCTION__); + return std::nullopt; + } + if (m_is_listening) { + LLDB_LOGF(log, "%s already listening", __PRETTY_FUNCTION__); + return std::nullopt; + } + m_is_listening = true; + LLDB_LOGF(log, "%s trying to listen on port 0", __PRETTY_FUNCTION__); + llvm::Expected> sock = + Socket::TcpListen("localhost:0", 5); + if (sock) { + GPUPluginConnectionInfo connection_info; + const uint16_t listen_port = (*sock)->GetLocalPortNumber(); + connection_info.connect_url = + llvm::formatv("connect://localhost:{}", listen_port); + LLDB_LOGF(log, "%s listening to %u", __PRETTY_FUNCTION__, listen_port); + // std::thread t(&LLDBServerPluginAMDGPU::AcceptAndMainLoopThread, this, + // std::move(*sock)); + // t.detach(); + + // Store the socket in the member variable to keep it alive + m_listen_socket = std::move(*sock); + auto extra_args = + llvm::formatv("gpu-url:connect://localhost:{};", listen_port); + m_is_connected = false; + llvm::Expected> res = + m_listen_socket->Accept( + m_main_loop, [this](std::unique_ptr socket) { + Log *log = GetLog(GDBRLog::Plugin); + LLDB_LOGF(log, + "LLDBServerPluginAMDGPU::AcceptAndMainLoopThread() " + "initializing connection"); + std::unique_ptr connection_up( + new ConnectionFileDescriptor(socket.release())); + this->m_gdb_server->InitializeConnection( + std::move(connection_up)); + m_is_connected = true; + }); + if (res) { + m_read_handles = std::move(*res); + } else { + LLDB_LOGF( + log, + "LLDBServerPluginAMDGPU::GetConnectionURL() failed to accept: %s", + llvm::toString(res.takeError()).c_str()); + } + + return connection_info; + } else { + std::string error = llvm::toString(sock.takeError()); + LLDB_LOGF(log, "%s failed to listen to localhost:0: %s", + __PRETTY_FUNCTION__, error.c_str()); + } + m_is_listening = false; + return std::nullopt; +} + +std::optional LLDBServerPluginAMDGPU::NativeProcessIsStopping() { + Log *log = GetLog(GDBRLog::Plugin); + if (!m_is_connected) { + initRocm(); + ProcessManagerAMDGPU *manager = + (ProcessManagerAMDGPU *)m_process_manager_up.get(); + manager->m_debugger = this; + + GPUActions actions; + actions.plugin_name = GetPluginName(); + + Status error; + LLDBServerPluginAMDGPU *amdGPUPlugin = this; + m_gpu_event_io_obj_sp = std::make_shared(m_notifier_fd); + m_gpu_event_read_up = m_main_loop.RegisterReadObject( + m_gpu_event_io_obj_sp, + [amdGPUPlugin](MainLoopBase &) { + amdGPUPlugin->HandleEventFileDescriptorEvent( + amdGPUPlugin->m_notifier_fd); + }, + error); + if (error.Fail()) { + LLDB_LOGF(log, "LLDBServerPluginAMDGPU::NativeProcessIsStopping() failed " + "to RegisterReadObject"); + // TODO: how to report this error? + } else { + LLDB_LOGF( + log, + "LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU() faking launch..."); + ProcessLaunchInfo info; + info.GetFlags().Set(eLaunchFlagStopAtEntry | eLaunchFlagDebug | + eLaunchFlagDisableASLR); + Args args; + args.AppendArgument("/pretend/path/to/amdgpu"); + args.AppendArgument("--option1"); + args.AppendArgument("--option2"); + args.AppendArgument("--option3"); + info.SetArguments(args, true); + info.GetEnvironment() = Host::GetEnvironment(); + info.SetProcessID(m_gpu_pid.handle); + m_gdb_server->SetLaunchInfo(info); + Status error = m_gdb_server->LaunchProcess(); + if (error.Fail()) { + LLDB_LOGF(log, + "LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU() failed to " + "launch: %s", + error.AsCString()); + } else { + LLDB_LOGF(log, "LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU() " + "launched successfully"); + } + actions.connect_info = CreateConnection(); + } + return actions; + } else { + if (m_wait_for_gpu_internal_bp_stop && m_gpu_internal_bp.has_value()) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Please set gpu breakpoint at 0x%p", + (void *)m_gpu_internal_bp->addr); + GPUActions actions; + actions.plugin_name = GetPluginName(); + + GPUBreakpointByAddress bp_addr; + bp_addr.load_address = m_gpu_internal_bp->addr; + + GPUBreakpointInfo bp; + bp.identifier = "GPU loader breakpoint"; + bp.addr_info.emplace(bp_addr); + + std::vector breakpoints; + breakpoints.emplace_back(std::move(bp)); + + actions.breakpoints = std::move(breakpoints); + m_wait_for_gpu_internal_bp_stop = false; + return actions; + } + } + return std::nullopt; +} + +bool LLDBServerPluginAMDGPU::HandleGPUInternalBreakpointHit( + const GPUInternalBreakpoinInfo &bp, bool &has_new_libraries) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Hit GPU loader breakpoint at address: 0x%llx", bp.addr); + has_new_libraries = false; + amd_dbgapi_breakpoint_id_t breakpoint_id{bp.breakpoind_id}; + amd_dbgapi_breakpoint_action_t action; + + auto status = amd_dbgapi_report_breakpoint_hit( + breakpoint_id, reinterpret_cast(this), + &action); + + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "amd_dbgapi_report_breakpoint_hit failed: %d", status); + return false; + } + + if (action == AMD_DBGAPI_BREAKPOINT_ACTION_RESUME) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "AMD_DBGAPI_BREAKPOINT_ACTION_RESUME"); + return true; + } else if (action == AMD_DBGAPI_BREAKPOINT_ACTION_HALT) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "AMD_DBGAPI_BREAKPOINT_ACTION_HALT"); + + amd_dbgapi_event_id_t resume_event_id = + process_event_queue(AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME); + amd_dbgapi_event_processed(resume_event_id); + if (!GetGPUProcess()->GetGPUModules().empty()) { + has_new_libraries = true; + } + return true; + } else { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown action: %d", action); + return false; + } + return true; +} + +amd_dbgapi_event_id_t LLDBServerPluginAMDGPU::process_event_queue( + amd_dbgapi_event_kind_t until_event_kind) { + while (true) { + amd_dbgapi_event_id_t event_id; + amd_dbgapi_event_kind_t event_kind; + amd_dbgapi_status_t status = amd_dbgapi_process_next_pending_event( + m_gpu_pid, &event_id, &event_kind); + + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "amd_dbgapi_process_next_pending_event failed: %d", status); + return AMD_DBGAPI_EVENT_NONE; + } + + if (event_kind != AMD_DBGAPI_EVENT_KIND_NONE) + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "event_kind != AMD_DBGAPI_EVENT_KIND_NONE: %d", event_kind); + + if (event_id.handle == AMD_DBGAPI_EVENT_NONE.handle || + event_kind == until_event_kind) + return event_id; + + GetGPUProcess()->handleDebugEvent(event_id, event_kind); + amd_dbgapi_event_processed(event_id); + } + return AMD_DBGAPI_EVENT_NONE; +} + +bool LLDBServerPluginAMDGPU::SetGPUBreakpoint(uint64_t addr, + const uint8_t *bp_instruction, + size_t size) { + struct BreakpointInfo { + uint64_t addr; + std::vector original_bytes; + std::vector breakpoint_instruction; + std::optional gpu_breakpoint_id; + }; + + BreakpointInfo bp; + bp.addr = addr; + bp.breakpoint_instruction.assign(bp_instruction, bp_instruction + size); + bp.original_bytes.resize(size); + bp.gpu_breakpoint_id = + std::nullopt; // No GPU breakpoint ID for ptrace version + + auto pid = GetNativeProcess()->GetID(); + // Read original bytes word by word + std::vector original_words; + for (size_t i = 0; i < size; i += sizeof(long)) { + long word = ptrace(PTRACE_PEEKDATA, pid, addr + i, nullptr); + assert(word != -1 && errno == 0); + + original_words.push_back(word); + // Copy bytes from the word into our original_bytes + size_t bytes_to_copy = std::min(sizeof(long), size - i); + memcpy(&bp.original_bytes[i], &word, bytes_to_copy); + } + + // Write breakpoint instruction word by word + for (size_t i = 0; i < size; i += sizeof(long)) { + long word = original_words[i / sizeof(long)]; + size_t bytes_to_copy = std::min(sizeof(long), size - i); + memcpy(&word, &bp_instruction[i], bytes_to_copy); + + auto ret = ptrace(PTRACE_POKEDATA, pid, addr + i, word); + assert(ret != -1 && errno == 0); + } + return true; +} + +bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) { + // First get the architecture ID for this process + amd_dbgapi_architecture_id_t arch_id; + amd_dbgapi_status_t status = amd_dbgapi_get_architecture(0x02C, &arch_id); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + // Handle error + LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_get_architecture failed"); + return false; + } + + // Get breakpoint instruction + const uint8_t *bp_instruction; + status = amd_dbgapi_architecture_get_info( + arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION, + sizeof(bp_instruction), &bp_instruction); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION failed"); + return false; + } + + // Get breakpoint instruction size + size_t bp_size; + status = amd_dbgapi_architecture_get_info( + arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE, + sizeof(bp_size), &bp_size); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF( + GetLog(GDBRLog::Plugin), + "AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE failed"); + return false; + } + + // Now call SetGPUBreakpoint with the retrieved instruction and size + return SetGPUBreakpoint(addr, bp_instruction, bp_size); +} + +GPUPluginBreakpointHitResponse +LLDBServerPluginAMDGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) { + Log *log = GetLog(GDBRLog::Plugin); + std::string json_string; + std::string &bp_identifier = args.breakpoint.identifier; + llvm::raw_string_ostream os(json_string); + os << toJSON(args); + LLDB_LOGF(log, "LLDBServerPluginAMDGPU::BreakpointWasHit(\"%s\"):\nJSON:\n%s", + bp_identifier.c_str(), json_string.c_str()); + + GPUPluginBreakpointHitResponse response; + response.actions.plugin_name = GetPluginName(); + if (bp_identifier == "GPU loader breakpoint") { + bool has_new_libraries = false; + bool success = HandleGPUInternalBreakpointHit(m_gpu_internal_bp.value(), + has_new_libraries); + assert(success); + if (has_new_libraries) { + response.actions.wait_for_gpu_process_to_resume = true; + auto process = m_gdb_server->GetCurrentProcess(); + ThreadAMDGPU *thread = (ThreadAMDGPU *)process->GetCurrentThread(); + thread->SetStopReason(lldb::eStopReasonDynammicLoader); + process->Halt(); + } + } + return response; +} + +GPUActions LLDBServerPluginAMDGPU::GetInitializeActions() { + GPUActions init_actions; + init_actions.plugin_name = GetPluginName(); + + GPUBreakpointInfo bp1; + bp1.identifier = "gpu_initialize"; + bp1.name_info = {"a.out", "gpu_initialize"}; + bp1.symbol_names.push_back("gpu_shlib_load"); + init_actions.breakpoints.emplace_back(std::move(bp1)); + return init_actions; +} diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h new file mode 100644 index 0000000000000..e1f057e7f8956 --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h @@ -0,0 +1,147 @@ +//===-- LLDBServerPluginAMDGPU.h -------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINAMDGPU_H +#define LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINAMDGPU_H + +#include "Plugins/Process/gdb-remote/LLDBServerPlugin.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" +#include "ProcessAMDGPU.h" +#include + +// This is a mock GPU plugin that is used for testing the LLDBServerPlugin. It +// should be run with the following code as the main binary: +/* + +$ cat main.cpp +#include + +struct ShlibInfo { + const char *path = nullptr; + ShlibInfo *next = nullptr; +}; + +ShlibInfo g_shlib_list = { "/tmp/a.out", nullptr}; + +int gpu_initialize() { + return puts(__FUNCTION__); +} +int gpu_shlib_load() { + return puts(__FUNCTION__); +} +int main(int argc, const char **argv) { + gpu_initialize(); + gpu_shlib_load(); + return 0; // Break here +} + +$ clang++ -g -O0 -o a.out main.cpp +$ ninja lldb lldb-server +$ ./bin/lldb a.out -o 'b /Break here/ -o run + +*/ +// If the above code is run, you will be stopped at the breakpoint and the Mock +// GPU target will be selected. Try doing a "reg read --all" to see the state +// of the GPU registers. Then you can select the native process target with +// "target select 0" and issue commands to the native process, and then select +// the GPU target with "target select 1" and issue commands to the GPU target. + +namespace lldb_private { + +class TCPSocket; + +namespace lldb_server { + +class GPUIOObject : public IOObject { +public: + GPUIOObject(int notifier_fd) + : lldb_private::IOObject(eFDTypeSocket), m_notifier_fd(notifier_fd) {} + + Status Read(void *buf, size_t &num_bytes) override { + Status error; + return error; + } + Status Write(const void *buf, size_t &num_bytes) override { + Status error; + return error; + } + virtual bool IsValid() const override { return true; } + virtual Status Close() override { + Status error; + return error; + } + + virtual WaitableHandle GetWaitableHandle() override { return m_notifier_fd; } + +private: + int m_notifier_fd = -1; +}; + +class LLDBServerPluginAMDGPU : public LLDBServerPlugin { +public: + LLDBServerPluginAMDGPU(LLDBServerPlugin::GDBServer &native_process, + MainLoop &main_loop); + ~LLDBServerPluginAMDGPU() override; + llvm::StringRef GetPluginName() override; + int GetEventFileDescriptorAtIndex(size_t idx) override; + bool HandleEventFileDescriptorEvent(int fd) override; + GPUActions GetInitializeActions() override; + std::optional NativeProcessIsStopping() override; + GPUPluginBreakpointHitResponse + BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override; + + NativeProcessProtocol *GetNativeProcess() { + return m_native_process.GetCurrentProcess(); + } + ProcessAMDGPU *GetGPUProcess() { + return (ProcessAMDGPU *)m_gdb_server->GetCurrentProcess(); + } + + bool CreateGPUBreakpoint(uint64_t addr); + + // TODO: make this private + struct GPUInternalBreakpoinInfo { + uint64_t addr; + amd_dbgapi_breakpoint_id_t breakpoind_id; + }; + std::optional m_gpu_internal_bp; + bool m_wait_for_gpu_internal_bp_stop = false; + amd_dbgapi_architecture_id_t m_architecture_id = AMD_DBGAPI_ARCHITECTURE_NONE; + +private: + std::optional CreateConnection(); + void CloseFDs(); + void AcceptAndMainLoopThread(std::unique_ptr listen_socket_up); + + bool initRocm(); + bool HandleGPUInternalBreakpointHit(const GPUInternalBreakpoinInfo &bp, + bool &has_new_libraries); + amd_dbgapi_event_id_t + process_event_queue(amd_dbgapi_event_kind_t until_event_kind); + bool processGPUEvent(); + bool SetGPUBreakpoint(uint64_t addr, const uint8_t *bp_instruction, + size_t size); + + // Used with a socketpair to get events on the native ptrace event queue. + int m_fds[2] = {-1, -1}; + Status m_main_loop_status; + MainLoopBase::ReadHandleUP m_gpu_event_read_up; + std::vector m_read_handles; + std::unique_ptr m_listen_socket; // Keep socket alive for main_loop + std::shared_ptr m_gpu_event_io_obj_sp; + + amd_dbgapi_process_id_t m_gpu_pid = AMD_DBGAPI_PROCESS_NONE; + int m_notifier_fd = -1; +}; + +} // namespace lldb_server +} // namespace lldb_private + +#endif // LLDB_TOOLS_LLDB_SERVER_LLDBSERVERPLUGINAMDGPU_H diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp new file mode 100644 index 0000000000000..df6015aabb9ee --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp @@ -0,0 +1,557 @@ +//===-- ProcessAMDGPU.cpp --------------------------------------*- 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 "ProcessAMDGPU.h" +#include "ThreadAMDGPU.h" + +#include "LLDBServerPluginAMDGPU.h" +#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include "lldb/Host/ProcessLaunchInfo.h" +#include "lldb/Utility/ProcessInfo.h" +#include "lldb/Utility/Status.h" +#include "lldb/Utility/UnimplementedError.h" +#include "llvm/Support/Error.h" + +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace lldb_private::process_gdb_remote; + +ProcessAMDGPU::ProcessAMDGPU(lldb::pid_t pid, NativeDelegate &delegate, + LLDBServerPluginAMDGPU *plugin) + : NativeProcessProtocol(pid, -1, delegate), m_debugger(plugin) { + m_state = eStateStopped; + UpdateThreads(); +} + +Status ProcessAMDGPU::Resume(const ResumeActionList &resume_actions) { + SetState(StateType::eStateRunning, true); + ThreadAMDGPU *thread = (ThreadAMDGPU *)GetCurrentThread(); + thread->GetRegisterContext().InvalidateAllRegisters(); + // if (!m_debugger->resume_process()) { + // return Status::FromErrorString("resume_process failed"); + // } + return Status(); +} + +Status ProcessAMDGPU::Halt() { + SetState(StateType::eStateStopped, true); + return Status(); +} + +Status ProcessAMDGPU::Detach() { + SetState(StateType::eStateDetached, true); + return Status(); +} + +/// Sends a process a UNIX signal \a signal. +/// +/// \return +/// Returns an error object. +Status ProcessAMDGPU::Signal(int signo) { + return Status::FromErrorString("unimplemented"); +} + +/// Tells a process to interrupt all operations as if by a Ctrl-C. +/// +/// The default implementation will send a local host's equivalent of +/// a SIGSTOP to the process via the NativeProcessProtocol::Signal() +/// operation. +/// +/// \return +/// Returns an error object. +Status ProcessAMDGPU::Interrupt() { return Status(); } + +Status ProcessAMDGPU::Kill() { return Status(); } + +Status ProcessAMDGPU::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) { + return Status::FromErrorString("unimplemented"); +} + +Status ProcessAMDGPU::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) { + return Status::FromErrorString("unimplemented"); +} + +lldb::addr_t ProcessAMDGPU::GetSharedLibraryInfoAddress() { + return LLDB_INVALID_ADDRESS; +} + +size_t ProcessAMDGPU::UpdateThreads() { + if (m_threads.empty()) { + lldb::tid_t tid = 3456; + m_threads.push_back(std::make_unique(*this, 3456)); + // ThreadAMDGPU &thread = static_cast(*m_threads.back()); + SetCurrentThreadID(tid); + } + return m_threads.size(); +} + +const ArchSpec &ProcessAMDGPU::GetArchitecture() const { + m_arch = ArchSpec("amdgpu"); + return m_arch; +} + +// Breakpoint functions +Status ProcessAMDGPU::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) { + // TODO: fix the race condition of GPU module load, client lldb setting + // breakpoint then resume GPU connection. + bool success = m_debugger->CreateGPUBreakpoint(addr); + if (!success) { + return Status::FromErrorString("CreateGPUBreakpoint failed"); + } + return Status(); +} + +llvm::ErrorOr> +ProcessAMDGPU::GetAuxvData() const { + return nullptr; // TODO: try to return + // llvm::make_error(); +} + +Status ProcessAMDGPU::GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) { + return Status::FromErrorString("unimplemented"); +} + +Status ProcessAMDGPU::GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) { + return Status::FromErrorString("unimplemented"); +} + +void ProcessAMDGPU::SetLaunchInfo(ProcessLaunchInfo &launch_info) { + static_cast(m_process_info) = + static_cast(launch_info); +} + +bool ProcessAMDGPU::GetProcessInfo(ProcessInstanceInfo &proc_info) { + Log *log = GetLog(GDBRLog::Plugin); + LLDB_LOGF(log, "ProcessAMDGPU::%s() entered", __FUNCTION__); + m_process_info.SetProcessID(m_pid); + m_process_info.SetArchitecture(GetArchitecture()); + proc_info = m_process_info; + return true; +} + +static std::pair> +ParsePathname(const std::string &pathname) { + std::string file_path; + uint64_t offset = 0; + uint64_t size = 0; + + // Find the position of #offset= + size_t offset_pos = pathname.find("#offset="); + if (offset_pos != std::string::npos) { + // Extract the file path (remove file:// prefix if present) + std::string path = pathname.substr(0, offset_pos); + if (path.find("file://") == 0) { + file_path = path.substr(7); // Remove "file://" + } else { + file_path = path; + } + + // Extract offset + size_t size_pos = pathname.find("&size=", offset_pos); + if (size_pos != std::string::npos) { + std::string offset_str = + pathname.substr(offset_pos + 8, size_pos - (offset_pos + 8)); + std::string size_str = pathname.substr(size_pos + 6); + + offset = std::stoull(offset_str); + size = std::stoull(size_str); + } + } else { + // No offset/size parameters, just return the path + if (pathname.find("file://") == 0) { + file_path = pathname.substr(7); + } else { + file_path = pathname; + } + } + + return {file_path, {offset, size}}; +} + +std::optional +ProcessAMDGPU::GetGPUDynamicLoaderLibraryInfos( + const GPUDynamicLoaderArgs &args) { + Log *log = GetLog(GDBRLog::Plugin); + LLDB_LOGF(log, "ProcessAMDGPU::%s() entered", __FUNCTION__); + + GPUDynamicLoaderResponse response; + + // Access the GPU modules using the GetGPUModules() method + const auto &gpu_modules = m_gpu_modules; + + LLDB_LOGF(log, "ProcessAMDGPU::%s() found %zu GPU modules", __FUNCTION__, + gpu_modules.size()); + + // Convert each GPU module to an SVR4LibraryInfo object + for (const auto &[addr, module] : gpu_modules) { + if (module.is_loaded) { + auto file_components = ParsePathname(module.path); + std::string path; + for (char c : file_components.first) { + if (c == '#') + path += "%23"; + else if (c == '$') + path += "%24"; + else if (c == '}') + path += "%7D"; + else if (c == '&') + path += "&"; + else + path += c; + } + + GPUDynamicLoaderLibraryInfo lib_info; + lib_info.pathname = path; + lib_info.load = true; + lib_info.load_address = module.base_address; + lib_info.file_offset = file_components.second.first; + lib_info.file_size = file_components.second.second; + + LLDB_LOGF(log, + "ProcessAMDGPU::%s() adding library: path=%s, addr=0x%" PRIx64 + ", offset=%" PRIu64 ", size=%" PRIu64, + __FUNCTION__, lib_info.pathname.c_str(), + lib_info.load_address.value(), lib_info.file_offset.value(), + lib_info.file_size.value()); + + response.library_infos.push_back(lib_info); + } + } + + return response; +} + +llvm::Expected> +ProcessAMDGPU::GetLoadedSVR4Libraries() { + std::vector libraries; + + // Check if we have a valid debugger instance + if (!m_debugger) { + return libraries; // Return empty vector if no debugger + } + + // Access the GPU modules using the GetGPUModules() method + const auto &gpu_modules = GetGPUModules(); + + // Convert each GPU module to an SVR4LibraryInfo object + for (const auto &[addr, module] : gpu_modules) { + if (module.is_loaded) { + SVR4LibraryInfo lib_info; + std::string path; + for (char c : module.path) { + if (c == '#') + path += "%23"; + else if (c == '$') + path += "%24"; + else if (c == '}') + path += "%7D"; + else if (c == '&') + path += "&"; + else + path += c; + } + lib_info.name = path; + lib_info.link_map = addr; + lib_info.base_addr = module.base_address; + lib_info.ld_addr = + module.size; // Using size as ld_addr as in handleLibrariesSvr4Read + lib_info.next = 0; // No next link in our implementation + + libraries.push_back(lib_info); + } + } + + return libraries; +} + +llvm::Expected> +ProcessManagerAMDGPU::Launch( + ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate) { + lldb::pid_t pid = launch_info.GetProcessID(); + auto proc_up = + std::make_unique(pid, native_delegate, m_debugger); + proc_up->SetLaunchInfo(launch_info); + return proc_up; +} + +llvm::Expected> +ProcessManagerAMDGPU::Attach( + lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) { + return llvm::createStringError("Unimplemented function"); +} + +bool ProcessAMDGPU::handleWaveStop(amd_dbgapi_event_id_t eventId) { + amd_dbgapi_wave_id_t wave_id; + auto status = amd_dbgapi_event_get_info(eventId, AMD_DBGAPI_EVENT_INFO_WAVE, + sizeof(wave_id), &wave_id); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_event_get_info failed: %d", + status); + return false; + } + amd_dbgapi_wave_stop_reasons_t stop_reason; + status = amd_dbgapi_wave_get_info(wave_id, AMD_DBGAPI_WAVE_INFO_STOP_REASON, + sizeof(stop_reason), &stop_reason); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_wave_get_info failed: %d", + status); + return false; + } + if ((stop_reason & AMD_DBGAPI_WAVE_STOP_REASON_BREAKPOINT) != 0) { + // auto ip = getPC(); + uint64_t pc; + status = amd_dbgapi_wave_get_info(wave_id, AMD_DBGAPI_WAVE_INFO_PC, + sizeof(pc), &pc); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_wave_get_info failed: %d", + status); + exit(-1); + } + pc -= 4; + amd_dbgapi_register_id_t pc_register_id; + status = amd_dbgapi_architecture_get_info( + m_debugger->m_architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_PC_REGISTER, + sizeof(pc_register_id), &pc_register_id); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "amd_dbgapi_architecture_get_info failed: %d", status); + exit(-1); + } + status = + amd_dbgapi_write_register(wave_id, pc_register_id, 0, sizeof(pc), &pc); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_write_register failed: %d", + status); + exit(-1); + } + // RemoveGPUBreakpoint(pc); + // auto thread = std::make_unique(*this, wave_id.handle, + // wave_id); thread->SetStopReason(lldb::eStopReasonBreakpoint); + m_wave_ids.emplace_back(wave_id); + + if (m_threads.size() == 1 && m_gpu_state == State::Initializing) { + m_threads.clear(); + SetCurrentThreadID(wave_id.handle); + } + + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Wave stopped due to breakpoint at: 0x%llx with wave id: %llu " + "event id: %llu", + pc, wave_id.handle, eventId.handle); + return true; + } else { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Wave stopped due to unknown reason: %d", + stop_reason); + } + return false; +} + +static const char *event_kind_str(amd_dbgapi_event_kind_t kind) { + switch (kind) { + case AMD_DBGAPI_EVENT_KIND_NONE: + return "NONE"; + + case AMD_DBGAPI_EVENT_KIND_WAVE_STOP: + return "WAVE_STOP"; + + case AMD_DBGAPI_EVENT_KIND_WAVE_COMMAND_TERMINATED: + return "WAVE_COMMAND_TERMINATED"; + + case AMD_DBGAPI_EVENT_KIND_CODE_OBJECT_LIST_UPDATED: + return "CODE_OBJECT_LIST_UPDATED"; + + case AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME: + return "BREAKPOINT_RESUME"; + + case AMD_DBGAPI_EVENT_KIND_RUNTIME: + return "RUNTIME"; + + case AMD_DBGAPI_EVENT_KIND_QUEUE_ERROR: + return "QUEUE_ERROR"; + } + assert(!"unhandled amd_dbgapi_event_kind_t value"); +} + +bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId, + amd_dbgapi_event_kind_t eventKind) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "handleDebugEvent(%llu, %s)", + eventId.handle, event_kind_str(eventKind)); + bool result; + if (eventKind == AMD_DBGAPI_EVENT_KIND_NONE) + return result; + + amd_dbgapi_runtime_state_t runtimeState = AMD_DBGAPI_RUNTIME_STATE_UNLOADED; + + // Get runtime state for the event + amd_dbgapi_status_t status = + amd_dbgapi_event_get_info(eventId, AMD_DBGAPI_EVENT_INFO_RUNTIME_STATE, + sizeof(runtimeState), &runtimeState); + + if (status == AMD_DBGAPI_STATUS_SUCCESS) { + // Handle different runtime states + switch (runtimeState) { + case AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS: + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime loaded successfully"); + break; + case AMD_DBGAPI_RUNTIME_STATE_LOADED_ERROR_RESTRICTION: + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime load restricted"); + break; + case AMD_DBGAPI_RUNTIME_STATE_UNLOADED: + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime unloaded"); + break; + default: + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown runtime state: %d", + runtimeState); + break; + } + } + + // Handle event kinds + switch (eventKind) { + case AMD_DBGAPI_EVENT_KIND_WAVE_STOP: { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Wave stop event received"); + + // Handle wave stop + result = handleWaveStop(eventId); + m_gpu_state = State::GPUStopped; + break; + } + + // case AMD_DBGAPI_EVENT_KIND_BREAKPOINT: { + // std::cout << "Breakpoint event received" << std::endl; + + // // Get breakpoint information for this event + // amd_dbgapi_breakpoint_id_t breakpointId; + // if (amd_dbgapi_event_get_info(eventId, + // AMD_DBGAPI_EVENT_INFO_BREAKPOINT, + // sizeof(breakpointId), &breakpointId) + // == + // AMD_DBGAPI_STATUS_SUCCESS) { + // std::cout << "Breakpoint ID: " << breakpointId << std::endl; + // } + // break; + // } + + case AMD_DBGAPI_EVENT_KIND_RUNTIME: { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Runtime event received, runtimeState: %d", runtimeState); + + // Additional runtime-specific handling based on state + if (runtimeState == AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS) { + // Runtime is now loaded, we can set breakpoints or perform other + // initialization + } + break; + } + + case AMD_DBGAPI_EVENT_KIND_CODE_OBJECT_LIST_UPDATED: { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Code object event received"); + + amd_dbgapi_code_object_id_t *code_object_list; + size_t count; + + amd_dbgapi_process_id_t gpu_pid{GetID()}; + amd_dbgapi_status_t status = amd_dbgapi_process_code_object_list( + gpu_pid, &count, &code_object_list, nullptr); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Failed to get code object list: %d", + status); + return result; + } + + m_gpu_modules.clear(); + for (size_t i = 0; i < count; ++i) { + uint64_t l_addr; + char *uri_bytes; + + status = amd_dbgapi_code_object_get_info( + code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_LOAD_ADDRESS, + sizeof(l_addr), &l_addr); + if (status != AMD_DBGAPI_STATUS_SUCCESS) + continue; + + status = amd_dbgapi_code_object_get_info( + code_object_list[i], AMD_DBGAPI_CODE_OBJECT_INFO_URI_NAME, + sizeof(uri_bytes), &uri_bytes); + if (status != AMD_DBGAPI_STATUS_SUCCESS) + continue; + + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Code object %zu: %s at address %llu", + i, uri_bytes, l_addr); + + if (m_gpu_modules.find(l_addr) == m_gpu_modules.end()) { + GPUModule mod = parseCodeObjectUrl(uri_bytes, l_addr); + m_gpu_modules[l_addr] = mod; + } + } + break; + } + + default: + LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown event kind: %d", eventKind); + break; + } + return result; +} + +ProcessAMDGPU::GPUModule +ProcessAMDGPU::parseCodeObjectUrl(const std::string &url, + uint64_t load_address) { + GPUModule info; + info.path = url; + info.base_address = load_address; + info.offset = 0; + info.size = 0; + info.is_loaded = true; + + // Find offset parameter + size_t offset_pos = url.find("#offset="); + if (offset_pos != std::string::npos) { + offset_pos += 8; // Skip "#offset=" + size_t amp_pos = url.find('&', offset_pos); + std::string offset_str; + + if (amp_pos != std::string::npos) { + offset_str = url.substr(offset_pos, amp_pos - offset_pos); + } else { + offset_str = url.substr(offset_pos); + } + + // Handle hex format (0x prefix) + if (offset_str.substr(0, 2) == "0x") { + info.offset = std::stoull(offset_str.substr(2), nullptr, 16); + } else { + info.offset = std::stoull(offset_str); + } + } + + // Find size parameter + size_t size_pos = url.find("&size="); + if (size_pos != std::string::npos) { + size_pos += 6; // Skip "&size=" + std::string size_str = url.substr(size_pos); + info.size = std::stoull(size_str); + } + + return info; +} + +void ProcessAMDGPU::AddThread(amd_dbgapi_wave_id_t wave_id) { + auto thread = std::make_unique(*this, wave_id.handle, wave_id); + thread->SetStopReason(lldb::eStopReasonBreakpoint); + m_threads.emplace_back(std::move(thread)); +} diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h new file mode 100644 index 0000000000000..08e8dd8352420 --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h @@ -0,0 +1,146 @@ +//===-- ProcessAMDGPU.h ----------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TOOLS_LLDB_SERVER_PROCESSAMDGPU_H +#define LLDB_TOOLS_LLDB_SERVER_PROCESSAMDGPU_H + +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Utility/ProcessInfo.h" +#include + +namespace lldb_private { +namespace lldb_server { + +class LLDBServerPluginAMDGPU; +/// \class ProcessAMDGPU +/// Abstract class that extends \a NativeProcessProtocol for a mock GPU. This +/// class is used to unit testing the GPU plugins in lldb-server. +class ProcessAMDGPU : public NativeProcessProtocol { + // TODO: change NativeProcessProtocol::GetArchitecture() to return by value + mutable ArchSpec m_arch; + ProcessInstanceInfo m_process_info; + +public: + ProcessAMDGPU(lldb::pid_t pid, NativeDelegate &delegate, LLDBServerPluginAMDGPU *plugin); + + Status Resume(const ResumeActionList &resume_actions) override; + + Status Halt() override; + + Status Detach() override; + + /// Sends a process a UNIX signal \a signal. + /// + /// \return + /// Returns an error object. + Status Signal(int signo) override; + + /// Tells a process to interrupt all operations as if by a Ctrl-C. + /// + /// The default implementation will send a local host's equivalent of + /// a SIGSTOP to the process via the NativeProcessProtocol::Signal() + /// operation. + /// + /// \return + /// Returns an error object. + Status Interrupt() override; + + Status Kill() override; + + Status ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) override; + + Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size, + size_t &bytes_written) override; + + lldb::addr_t GetSharedLibraryInfoAddress() override; + + size_t UpdateThreads() override; + + const ArchSpec &GetArchitecture() const override; + + // Breakpoint functions + Status SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) override; + + llvm::ErrorOr> + GetAuxvData() const override; + + Status GetLoadedModuleFileSpec(const char *module_path, + FileSpec &file_spec) override; + + Status GetFileLoadAddress(const llvm::StringRef &file_name, + lldb::addr_t &load_addr) override; + + bool GetProcessInfo(ProcessInstanceInfo &info) override; + + // Custom accessors + void SetLaunchInfo(ProcessLaunchInfo &launch_info); + + llvm::Expected> + GetLoadedSVR4Libraries() override; + + std::optional + GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args) override; + + bool handleWaveStop(amd_dbgapi_event_id_t eventId); + + bool handleDebugEvent(amd_dbgapi_event_id_t eventId, + amd_dbgapi_event_kind_t eventKind); + + struct GPUModule { + std::string path; + uint64_t base_address; + uint64_t offset; + uint64_t size; + bool is_loaded; + }; + std::unordered_map& GetGPUModules() { + return m_gpu_modules; + } + + GPUModule parseCodeObjectUrl(const std::string &url, uint64_t load_address); + void AddThread(amd_dbgapi_wave_id_t wave_id); + + LLDBServerPluginAMDGPU* m_debugger = nullptr; + std::unordered_map m_gpu_modules; + + enum class State { + Initializing, + ModuleLoadStopped, + Running, + GPUStopped, + }; + State m_gpu_state = State::Initializing; + std::vector m_wave_ids; +}; + +class ProcessManagerAMDGPU : public NativeProcessProtocol::Manager { +public: + ProcessManagerAMDGPU(MainLoop &mainloop) + : NativeProcessProtocol::Manager(mainloop) {} + + llvm::Expected> + Launch(ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate &native_delegate) override; + + NativeProcessProtocol::Extension GetSupportedExtensions() const override { + return NativeProcessProtocol::Extension::gpu_dyld; + } + + llvm::Expected> + Attach(lldb::pid_t pid, + NativeProcessProtocol::NativeDelegate &native_delegate) override; + + LLDBServerPluginAMDGPU* m_debugger = nullptr; +}; + +} // namespace lldb_server +} // namespace lldb_private + +#endif diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp new file mode 100644 index 0000000000000..e34728b5ff85d --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp @@ -0,0 +1,517 @@ +//===-- RegisterContextAMDGPU.cpp ----------------------------------------===// +// +// 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 "RegisterContextAMDGPU.h" + +#include "LLDBServerPluginAMDGPU.h" +#include "ProcessAMDGPU.h" +#include "ThreadAMDGPU.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/common/NativeProcessProtocol.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/Status.h" + +#include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" +#include +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::lldb_server; +using namespace lldb_private::process_gdb_remote; + +static size_t kNumRegs = 0; +static std::vector g_reg_sets; +/// Define all of the information about all registers. The register info structs +/// are accessed by the LLDB register numbers, which are defined above. +static std::vector g_reg_infos; +size_t g_register_buffer_size = 0; +static std::unordered_map + g_lldb_num_to_amd_reg_id; + +bool RegisterContextAMDGPU::InitRegisterInfos() { + if (!g_reg_infos.empty()) + return true; + amd_dbgapi_status_t status; + ThreadAMDGPU *thread = (ThreadAMDGPU *)&m_thread; + amd_dbgapi_architecture_id_t architecture_id = + thread->GetProcess().m_debugger->m_architecture_id; + // Define custom hash functions for register IDs + struct RegisterClassIdHash { + std::size_t operator()(const amd_dbgapi_register_class_id_t &id) const { + return std::hash{}(id.handle); + } + }; + struct RegisterClassIdEqual { + bool operator()(const amd_dbgapi_register_class_id_t &lhs, + const amd_dbgapi_register_class_id_t &rhs) const { + return lhs.handle == rhs.handle; + } + }; + + struct RegisterIdHash { + std::size_t operator()(const amd_dbgapi_register_id_t &id) const { + return std::hash{}(id.handle); + } + }; + struct RegisterIdEqual { + bool operator()(const amd_dbgapi_register_id_t &lhs, + const amd_dbgapi_register_id_t &rhs) const { + return lhs.handle == rhs.handle; + } + }; + + /* Get register class ids. */ + size_t register_class_count; + amd_dbgapi_register_class_id_t *register_class_ids; + status = amd_dbgapi_architecture_register_class_list( + architecture_id, ®ister_class_count, ®ister_class_ids); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Failed to get register class list from amd-dbgapi"); + return false; + } + + // Get register class names. + std::unordered_map + register_class_names; + for (size_t i = 0; i < register_class_count; ++i) { + char *bytes; + status = amd_dbgapi_architecture_register_class_get_info( + register_class_ids[i], AMD_DBGAPI_REGISTER_CLASS_INFO_NAME, + sizeof(bytes), &bytes); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Failed to get register class name from amd-dbgapi"); + return false; + } + + // gdb::unique_xmalloc_ptr name(bytes); + register_class_names.emplace(register_class_ids[i], bytes); + } + + /* Get all register count. */ + size_t register_count; + amd_dbgapi_register_id_t *register_ids; + status = amd_dbgapi_architecture_register_list( + architecture_id, ®ister_count, ®ister_ids); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Failed to get register list from amd-dbgapi"); + return false; + } + kNumRegs = register_count; + + std::unordered_map, RegisterClassIdHash, + RegisterClassIdEqual> + register_class_to_register_ids; + for (size_t i = 0; i < register_class_count; ++i) { + for (size_t j = 0; j < register_count; ++j) { + amd_dbgapi_register_class_state_t register_class_state; + status = amd_dbgapi_register_is_in_register_class( + register_class_ids[i], register_ids[j], ®ister_class_state); + if (status == AMD_DBGAPI_STATUS_SUCCESS && + register_class_state == AMD_DBGAPI_REGISTER_CLASS_STATE_MEMBER) { + register_class_to_register_ids[register_class_ids[i]].push_back( + register_ids[j]); + break; // TODO: can a register be in multiple classes? + } + } + } + + std::vector all_register_properties; + all_register_properties.resize(register_count); + for (size_t regnum = 0; regnum < register_count; ++regnum) { + auto ®ister_properties = all_register_properties[regnum]; + if (amd_dbgapi_register_get_info( + register_ids[regnum], AMD_DBGAPI_REGISTER_INFO_PROPERTIES, + sizeof(register_properties), + ®ister_properties) != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Failed to get register properties from amd-dbgapi"); + return false; + } + } + + std::vector dwarf_regnum_to_gdb_regnum; + std::unordered_map + register_names; + for (size_t i = 0; i < register_count; ++i) { + /* Get register name. */ + char *bytes; + status = amd_dbgapi_register_get_info( + register_ids[i], AMD_DBGAPI_REGISTER_INFO_NAME, sizeof(bytes), &bytes); + if (status == AMD_DBGAPI_STATUS_SUCCESS) { + register_names[register_ids[i]] = bytes; + free(bytes); + } + + /* Get register DWARF number. */ + uint64_t dwarf_num; + status = amd_dbgapi_register_get_info(register_ids[i], + AMD_DBGAPI_REGISTER_INFO_DWARF, + sizeof(dwarf_num), &dwarf_num); + if (status == AMD_DBGAPI_STATUS_SUCCESS) { + if (dwarf_num >= dwarf_regnum_to_gdb_regnum.size()) + dwarf_regnum_to_gdb_regnum.resize(dwarf_num + 1, -1); + + dwarf_regnum_to_gdb_regnum[dwarf_num] = i; + } + } + + amd_dbgapi_register_id_t pc_register_id; + status = amd_dbgapi_architecture_get_info( + architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_PC_REGISTER, + sizeof(pc_register_id), &pc_register_id); + if (status != AMD_DBGAPI_STATUS_SUCCESS) { + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Failed to get PC register from amd-dbgapi"); + return false; + } + // Initialize g_reg_infos with register information from AMD dbgapi + g_reg_infos.resize(register_count); + + // Map from register class ID to register numbers for that class + std::unordered_map, + RegisterClassIdHash, RegisterClassIdEqual> + register_class_to_lldb_regnums; + // Populate g_reg_infos with register information from AMD dbgapi + for (size_t i = 0; i < register_count; ++i) { + amd_dbgapi_register_id_t reg_id = register_ids[i]; + RegisterInfo ®_info = g_reg_infos[i]; + + // Set register name from AMD dbgapi + auto name_it = register_names.find(reg_id); + if (name_it != register_names.end()) { + reg_info.name = strdup(name_it->second.c_str()); + reg_info.alt_name = nullptr; + } else { + // Fallback name if not found + char name[16]; + snprintf(name, sizeof(name), "reg%zu", i); + reg_info.name = strdup(name); + reg_info.alt_name = nullptr; + } + + // Get register size from AMD dbgapi + uint64_t reg_size; + status = amd_dbgapi_register_get_info(reg_id, AMD_DBGAPI_REGISTER_INFO_SIZE, + sizeof(reg_size), ®_size); + if (status == AMD_DBGAPI_STATUS_SUCCESS) { + reg_info.byte_size = reg_size; + } else { + reg_info.byte_size = 8; // Default to 64-bit registers + } + reg_info.byte_offset = g_register_buffer_size; // Simple offset calculation + g_register_buffer_size += reg_info.byte_size; + + // Set encoding and format based on register name + std::string reg_name = + name_it != register_names.end() ? name_it->second : ""; + + // Check if register name contains indicators of its type + // TODO: is this the correct way to do this? + if (reg_name.find("float") != std::string::npos || + reg_name.find("fp") != std::string::npos) { + reg_info.encoding = eEncodingIEEE754; + reg_info.format = eFormatFloat; + } else if (reg_name.find("vec") != std::string::npos || + reg_name.find("simd") != std::string::npos) { + reg_info.encoding = eEncodingVector; + reg_info.format = eFormatVectorOfUInt8; + } else if (reg_info.byte_size > 8) { + // TODO: check AMD_DBGAPI_REGISTER_INFO_TYPE and assign encoding/format. + reg_info.encoding = eEncodingVector; + reg_info.format = eFormatVectorOfUInt8; + } else { + // Default for other types + reg_info.encoding = eEncodingUint; + reg_info.format = eFormatHex; + } + + // Set register kinds + reg_info.kinds[eRegisterKindLLDB] = i; // LLDB register number is the index + g_lldb_num_to_amd_reg_id[i] = + reg_id; // Map from LLDB register number to AMD + + // Set DWARF register number if available + uint64_t dwarf_num; + status = amd_dbgapi_register_get_info( + reg_id, AMD_DBGAPI_REGISTER_INFO_DWARF, sizeof(dwarf_num), &dwarf_num); + if (status == AMD_DBGAPI_STATUS_SUCCESS) { + reg_info.kinds[eRegisterKindDWARF] = dwarf_num; + } else { + reg_info.kinds[eRegisterKindDWARF] = LLDB_INVALID_REGNUM; + } + + // Set EH_FRAME register number (same as DWARF for now) + reg_info.kinds[eRegisterKindEHFrame] = reg_info.kinds[eRegisterKindDWARF]; + + // Set generic register kind + reg_info.kinds[eRegisterKindGeneric] = LLDB_INVALID_REGNUM; + + // Check if this is the PC register + if (reg_id.handle == pc_register_id.handle) { + reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + } + + // Add this register indices belong to its register classes + for (size_t j = 0; j < register_class_count; ++j) { + amd_dbgapi_register_class_state_t register_class_state; + status = amd_dbgapi_register_is_in_register_class( + register_class_ids[j], reg_id, ®ister_class_state); + if (status == AMD_DBGAPI_STATUS_SUCCESS && + register_class_state == AMD_DBGAPI_REGISTER_CLASS_STATE_MEMBER) { + register_class_to_lldb_regnums[register_class_ids[j]].push_back(i); + } + } + } + + // Create register sets from register classes + g_reg_sets.clear(); + + for (size_t i = 0; i < register_class_count; ++i) { + auto class_id = register_class_ids[i]; + auto name_it = register_class_names.find(class_id); + if (name_it == register_class_names.end()) { + continue; // Skip if no name found + } + + auto regnums_it = register_class_to_lldb_regnums.find(class_id); + if (regnums_it == register_class_to_lldb_regnums.end() || + regnums_it->second.empty()) { + continue; // Skip if no registers in this class + } + + // Create a new register set for this class + RegisterSet reg_set; + reg_set.name = strdup(name_it->second.c_str()); + + // Create short name from the full name (use first word or first few chars) + std::string short_name = name_it->second; + size_t space_pos = short_name.find(' '); + if (space_pos != std::string::npos) { + short_name = short_name.substr(0, space_pos); + } else if (short_name.length() > 3) { + short_name = short_name.substr(0, 3); + } + std::transform(short_name.begin(), short_name.end(), short_name.begin(), + ::tolower); + reg_set.short_name = strdup(short_name.c_str()); + + // Get register numbers for this class + const auto ®nums = regnums_it->second; + + // Store register numbers in a static container to ensure they live + // for the duration of the program + static std::vector> all_reg_nums; + all_reg_nums.push_back(regnums); + + // Point the RegisterSet's registers field to the data in our static vector + reg_set.registers = all_reg_nums.back().data(); + reg_set.num_registers = all_reg_nums.back().size(); + g_reg_sets.push_back(reg_set); + } + return true; +} + +RegisterContextAMDGPU::RegisterContextAMDGPU( + NativeThreadProtocol &native_thread) + : NativeRegisterContext(native_thread) { + InitRegisterInfos(); + InitRegisters(); + // Only doing this for the Mock GPU class, don't do this in real GPU classes. + // ReadRegs(); +} + +void RegisterContextAMDGPU::InitRegisters() { + m_regs.data.resize(g_register_buffer_size); + m_regs_valid.resize(kNumRegs, false); +} + +void RegisterContextAMDGPU::InvalidateAllRegisters() { + // Do what ever book keeping we need to do to indicate that all register + // values are now invalid. + for (uint32_t i = 0; i < kNumRegs; ++i) + m_regs_valid[i] = false; +} + +Status RegisterContextAMDGPU::ReadReg(const RegisterInfo *reg_info) { + Status error; + const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB]; + assert(lldb_reg_num < kNumRegs); + auto amd_reg_id = g_lldb_num_to_amd_reg_id[lldb_reg_num]; + ThreadAMDGPU *thread = (ThreadAMDGPU *)&m_thread; + auto wave_id = thread->GetWaveId(); + if (!wave_id) { + // Swallow the error because so that we are returning the dummy register + // vlaues. + return error; + } + amd_dbgapi_register_exists_t exists; + amd_dbgapi_status_t amd_status = + amd_dbgapi_wave_register_exists(wave_id.value(), amd_reg_id, &exists); + if (amd_status != AMD_DBGAPI_STATUS_SUCCESS) { + error.FromErrorStringWithFormat( + "Failed to check register %s existence due to error %d", + reg_info->name, amd_status); + return error; + } + if (exists != AMD_DBGAPI_REGISTER_PRESENT) { + error = Status::FromErrorStringWithFormat( + "Failed to read register %s due to register not present", + reg_info->name); + return error; + } + + amd_status = amd_dbgapi_prefetch_register(wave_id.value(), amd_reg_id, + m_regs.data.size() - lldb_reg_num); + if (amd_status != AMD_DBGAPI_STATUS_SUCCESS) { + error = Status::FromErrorStringWithFormat( + "Failed to prefetch register %s due to error %d", reg_info->name, + amd_status); + return error; + } + + // Read the register value + amd_status = amd_dbgapi_read_register( + wave_id.value(), amd_reg_id, 0, reg_info->byte_size, + m_regs.data.data() + reg_info->byte_offset); + if (amd_status != AMD_DBGAPI_STATUS_SUCCESS) { + error = Status::FromErrorStringWithFormat( + "Failed to read register %s due to error %d", reg_info->name, + amd_status); + return error; + } + m_regs_valid[lldb_reg_num] = true; + return error; +} + +Status RegisterContextAMDGPU::ReadRegs() { + ThreadAMDGPU *thread = (ThreadAMDGPU *)&m_thread; + if (thread != nullptr) { + auto wave_id = thread->GetWaveId(); + bool wave_stopped = wave_id.has_value(); + if (wave_stopped) { + for (uint32_t i = 0; i < g_reg_infos.size(); ++i) { + Status error = ReadReg(&g_reg_infos[i]); + if (error.Fail()) + return error; + } + } + } else { + // Fill all registers with unique values. + for (uint32_t i = 0; i < g_reg_infos.size(); ++i) { + memcpy(m_regs.data.data() + g_reg_infos[i].byte_offset, &i, sizeof(i)); + } + } + return Status(); +} + +uint32_t RegisterContextAMDGPU::GetRegisterSetCount() const { + return g_reg_sets.size(); +} + +uint32_t RegisterContextAMDGPU::GetRegisterCount() const { return kNumRegs; } + +uint32_t RegisterContextAMDGPU::GetUserRegisterCount() const { + return GetRegisterCount(); +} + +const RegisterInfo * +RegisterContextAMDGPU::GetRegisterInfoAtIndex(uint32_t reg) const { + if (reg < kNumRegs) + return &g_reg_infos[reg]; + return nullptr; +} + +const RegisterSet * +RegisterContextAMDGPU::GetRegisterSet(uint32_t set_index) const { + if (set_index < GetRegisterSetCount()) + return &g_reg_sets[set_index]; + return nullptr; +} + +Status RegisterContextAMDGPU::ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) { + Status error; + const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB]; + if (!m_regs_valid[lldb_reg_num]) { + error = ReadReg(reg_info); + } + if (error.Fail()) + return error; + reg_value.SetBytes(m_regs.data.data() + reg_info->byte_offset, + reg_info->byte_size, lldb::eByteOrderLittle); + return Status(); +} + +Status RegisterContextAMDGPU::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) { + const uint32_t lldb_reg_num = reg_info->kinds[eRegisterKindLLDB]; + const void *new_value = reg_value.GetBytes(); + memcpy(m_regs.data.data() + reg_info->byte_offset, new_value, + reg_info->byte_size); + m_regs_valid[lldb_reg_num] = true; + return Status(); +} + +Status RegisterContextAMDGPU::ReadAllRegisterValues( + lldb::WritableDataBufferSP &data_sp) { + ReadRegs(); // Read all registers first + const size_t regs_byte_size = m_regs.data.size(); + data_sp.reset(new DataBufferHeap(regs_byte_size, 0)); + uint8_t *dst = data_sp->GetBytes(); + memcpy(dst, &m_regs.data[0], regs_byte_size); + return Status(); +} + +Status RegisterContextAMDGPU::WriteAllRegisterValues( + const lldb::DataBufferSP &data_sp) { + const size_t regs_byte_size = m_regs.data.size(); + + if (!data_sp) { + return Status::FromErrorStringWithFormat( + "RegisterContextAMDGPU::%s invalid data_sp provided", __FUNCTION__); + } + + if (data_sp->GetByteSize() != regs_byte_size) { + return Status::FromErrorStringWithFormat( + "RegisterContextAMDGPU::%s data_sp contained mismatched " + "data size, expected %" PRIu64 ", actual %" PRIu64, + __FUNCTION__, regs_byte_size, data_sp->GetByteSize()); + } + + const uint8_t *src = data_sp->GetBytes(); + if (src == nullptr) { + return Status::FromErrorStringWithFormat( + "RegisterContextAMDGPU::%s " + "DataBuffer::GetBytes() returned a null " + "pointer", + __FUNCTION__); + } + memcpy(&m_regs.data[0], src, regs_byte_size); + return Status(); +} + +std::vector +RegisterContextAMDGPU::GetExpeditedRegisters(ExpeditedRegs expType) const { + static std::vector g_expedited_regs; + if (g_expedited_regs.empty()) { + // TODO: is this the correct way to do this? + // g_expedited_regs.push_back(LLDB_REGNUM_GENERIC_PC); + // g_expedited_regs.push_back(LLDB_SP); + // g_expedited_regs.push_back(LLDB_FP); + } + return g_expedited_regs; +} diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h new file mode 100644 index 0000000000000..938d6d6bc7e0b --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.h @@ -0,0 +1,65 @@ +//===-- RegisterContextAMDGPU.h --------------------------------*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TOOLS_LLDB_SERVER_REGISTERCONTEXTAMDGPU_H +#define LLDB_TOOLS_LLDB_SERVER_REGISTERCONTEXTAMDGPU_H + +// #include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h" +#include "lldb/Host/common/NativeRegisterContext.h" +#include "lldb/lldb-forward.h" + +namespace lldb_private { +namespace lldb_server { + +class RegisterContextAMDGPU : public NativeRegisterContext { +public: + RegisterContextAMDGPU(NativeThreadProtocol &native_thread); + + uint32_t GetRegisterCount() const override; + + uint32_t GetUserRegisterCount() const override; + + const RegisterInfo *GetRegisterInfoAtIndex(uint32_t reg) const override; + + uint32_t GetRegisterSetCount() const override; + + const RegisterSet *GetRegisterSet(uint32_t set_index) const override; + + Status ReadRegister(const RegisterInfo *reg_info, + RegisterValue ®_value) override; + + Status WriteRegister(const RegisterInfo *reg_info, + const RegisterValue ®_value) override; + + Status ReadAllRegisterValues(lldb::WritableDataBufferSP &data_sp) override; + + Status WriteAllRegisterValues(const lldb::DataBufferSP &data_sp) override; + + std::vector + GetExpeditedRegisters(ExpeditedRegs expType) const override; + + void InvalidateAllRegisters(); + +private: + bool InitRegisterInfos(); + void InitRegisters(); + + Status ReadRegs(); + Status ReadReg(const RegisterInfo *reg_info); + + // All AMD GPU registers are contained in this buffer. + struct { + std::vector data; + } m_regs; + std::vector m_regs_valid; +}; + +} // namespace lldb_server +} // namespace lldb_private + +#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_REGISTERCONTEXTAMDGPU_H diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp new file mode 100644 index 0000000000000..0c724afa95894 --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.cpp @@ -0,0 +1,63 @@ +//===-- ThreadAMDGPU.cpp ------------------------------------- -*- 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 "ThreadAMDGPU.h" +#include "ProcessAMDGPU.h" + +using namespace lldb_private; +using namespace lldb_server; + +ThreadAMDGPU::ThreadAMDGPU(ProcessAMDGPU &process, lldb::tid_t tid, + std::optional wave_id) + : NativeThreadProtocol(process, tid), m_reg_context(*this), + m_wave_id(wave_id) { + m_stop_info.reason = lldb::eStopReasonSignal; + m_stop_info.signo = SIGTRAP; +} + +// NativeThreadProtocol Interface +std::string ThreadAMDGPU::GetName() { + if (!m_wave_id) + return "AMD Native Shadow Thread"; + else + return std::string("AMD GPU Thread ") + + std::to_string(m_wave_id.value().handle); +} + +lldb::StateType ThreadAMDGPU::GetState() { return lldb::eStateStopped; } + +bool ThreadAMDGPU::GetStopReason(ThreadStopInfo &stop_info, + std::string &description) { + stop_info = m_stop_info; + description = m_description; + return true; +} + +Status ThreadAMDGPU::SetWatchpoint(lldb::addr_t addr, size_t size, + uint32_t watch_flags, bool hardware) { + return Status::FromErrorString("unimplemented"); +} + +Status ThreadAMDGPU::RemoveWatchpoint(lldb::addr_t addr) { + return Status::FromErrorString("unimplemented"); +} + +Status ThreadAMDGPU::SetHardwareBreakpoint(lldb::addr_t addr, size_t size) { + return Status::FromErrorString("unimplemented"); +} + +Status ThreadAMDGPU::RemoveHardwareBreakpoint(lldb::addr_t addr) { + return Status::FromErrorString("unimplemented"); +} + +ProcessAMDGPU &ThreadAMDGPU::GetProcess() { + return static_cast(m_process); +} + +const ProcessAMDGPU &ThreadAMDGPU::GetProcess() const { + return static_cast(m_process); +} diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h new file mode 100644 index 0000000000000..02991a20bfdd9 --- /dev/null +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ThreadAMDGPU.h @@ -0,0 +1,75 @@ +//===-- ThreadAMDGPU.h --------------------------------------- -*- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_TOOLS_LLDB_SERVER_THREADAMDGPU_H +#define LLDB_TOOLS_LLDB_SERVER_THREADAMDGPU_H + +#include "RegisterContextAMDGPU.h" +#include "lldb/Host/common/NativeThreadProtocol.h" +#include "lldb/lldb-private-forward.h" +#include +#include + +namespace lldb_private { +namespace lldb_server { +class ProcessAMDGPU; + +class NativeProcessLinux; + +class ThreadAMDGPU : public NativeThreadProtocol { + friend class ProcessAMDGPU; + +public: + ThreadAMDGPU(ProcessAMDGPU &process, lldb::tid_t tid, std::optional wave_id = std::nullopt); + + // NativeThreadProtocol Interface + std::string GetName() override; + + lldb::StateType GetState() override; + + bool GetStopReason(ThreadStopInfo &stop_info, + std::string &description) override; + + void SetStopReason(lldb::StopReason reason) { + m_stop_info.reason = reason; + } + + RegisterContextAMDGPU &GetRegisterContext() override { + return m_reg_context; + } + + Status SetWatchpoint(lldb::addr_t addr, size_t size, uint32_t watch_flags, + bool hardware) override; + + Status RemoveWatchpoint(lldb::addr_t addr) override; + + Status SetHardwareBreakpoint(lldb::addr_t addr, size_t size) override; + + Status RemoveHardwareBreakpoint(lldb::addr_t addr) override; + + ProcessAMDGPU &GetProcess(); + + const ProcessAMDGPU &GetProcess() const; + + std::optional GetWaveId() const { + return m_wave_id; + } + +private: + // Member Variables + lldb::StateType m_state; + ThreadStopInfo m_stop_info; + std::string m_description = ""; + RegisterContextAMDGPU m_reg_context; + std::string m_stop_description; + std::optional m_wave_id; +}; +} // namespace lldb_server +} // namespace lldb_private + +#endif // #ifndef LLDB_TOOLS_LLDB_SERVER_THREADAMDGPU_H diff --git a/lldb/tools/lldb-server/Plugins/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/CMakeLists.txt index 45f42a7afd1fa..0d8efaaff13c1 100644 --- a/lldb/tools/lldb-server/Plugins/CMakeLists.txt +++ b/lldb/tools/lldb-server/Plugins/CMakeLists.txt @@ -1 +1,5 @@ -add_subdirectory(MockGPU) +if(DEFINED ROCM_PATH AND EXISTS ${ROCM_PATH}) + add_subdirectory(AMDGPU) +else() + add_subdirectory(MockGPU) +endif() diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp index f06d7fc4e5682..75d5f473f0cc4 100644 --- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp +++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.cpp @@ -26,8 +26,8 @@ using namespace lldb_private::lldb_server; using namespace lldb_private::process_gdb_remote; LLDBServerPluginMockGPU::LLDBServerPluginMockGPU( - LLDBServerPlugin::GDBServer &native_process) - : LLDBServerPlugin(native_process) { + LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop) + : LLDBServerPlugin(native_process, main_loop) { m_process_manager_up.reset(new ProcessMockGPU::Manager(m_main_loop)); m_gdb_server.reset(new GDBRemoteCommunicationServerLLGS( m_main_loop, *m_process_manager_up, "mock-gpu.server")); diff --git a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h index 620fdcaf9e7c4..55c5307afa6c4 100644 --- a/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h +++ b/lldb/tools/lldb-server/Plugins/MockGPU/LLDBServerPluginMockGPU.h @@ -61,7 +61,7 @@ namespace lldb_server { class LLDBServerPluginMockGPU : public LLDBServerPlugin { public: - LLDBServerPluginMockGPU(LLDBServerPlugin::GDBServer &native_process); + LLDBServerPluginMockGPU(LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop); ~LLDBServerPluginMockGPU() override; llvm::StringRef GetPluginName() override; int GetEventFileDescriptorAtIndex(size_t idx) override; diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index 07a0a18818fa3..11b17f05bb92c 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -18,7 +18,6 @@ #endif #include "LLDBServerUtilities.h" -#include "Plugins/MockGPU/LLDBServerPluginMockGPU.h" #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h" #include "Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h" #include "lldb/Host/Config.h" @@ -48,7 +47,13 @@ #include "Plugins/Process/Windows/Common/NativeProcessWindows.h" #endif -#include "Plugins/MockGPU/ProcessMockGPU.h" +#if defined(LLDB_ENABLE_AMDGPU_PLUGIN) +#include "Plugins/AMDGPU/LLDBServerPluginAMDGPU.h" +typedef lldb_private::lldb_server::LLDBServerPluginAMDGPU LLDBServerGPUPlugin; +#elif defined(LLDB_ENABLE_MOCKGPU_PLUGIN) +#include "Plugins/MockGPU/LLDBServerPluginMockGPU.h" +typedef lldb_private::lldb_server::LLDBServerPluginMockGPU LLDBServerGPUPlugin; +#endif #ifndef LLGS_PROGRAM_NAME #define LLGS_PROGRAM_NAME "lldb-server" @@ -438,7 +443,7 @@ int main_gdbserver(int argc, char *argv[]) { if (!LLDBServerUtilities::SetupLogging( log_file, log_channels, LLDB_LOG_OPTION_PREPEND_TIMESTAMP | - LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD)) + LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD)) return -1; std::vector Inputs; @@ -456,8 +461,11 @@ int main_gdbserver(int argc, char *argv[]) { NativeProcessManager manager(mainloop); GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager, "gdb-server"); - // Install the mock GPU plugin. - gdb_server.InstallPlugin(std::make_unique(gdb_server)); +#if defined(LLDB_ENABLE_AMDGPU_PLUGIN) || defined(LLDB_ENABLE_MOCKGPU_PLUGIN) + // Install GPU plugin. + gdb_server.InstallPlugin( + std::make_unique(gdb_server, mainloop)); +#endif llvm::StringRef host_and_port; if (!Inputs.empty() && connection_fd == SharedSocket::kInvalidFD) { diff --git a/llvm/cmake/modules/CrossCompile.cmake b/llvm/cmake/modules/CrossCompile.cmake index 3b31d3e218a37..cb52a88ef81c7 100644 --- a/llvm/cmake/modules/CrossCompile.cmake +++ b/llvm/cmake/modules/CrossCompile.cmake @@ -100,6 +100,7 @@ function(llvm_create_cross_target project_name target_name toolchain buildtype) -DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN="${LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN}" -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_TESTS=OFF + -DROCM_PATH=${ROCM_PATH} ${build_type_flags} ${linker_flag} ${external_clang_dir} ${libc_flags} ${ARGN} WORKING_DIRECTORY ${${project_name}_${target_name}_BUILD} From ca0b83a0f58bf762fd490d03706c9a305983d537 Mon Sep 17 00:00:00 2001 From: Jeffrey Tan Date: Tue, 10 Jun 2025 15:49:36 -0700 Subject: [PATCH 2/3] Feedback from Greg --- .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp | 33 ++++++++++--------- .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.h | 2 +- .../Plugins/AMDGPU/ProcessAMDGPU.cpp | 21 ++++++------ .../Plugins/AMDGPU/RegisterContextAMDGPU.cpp | 10 +++--- lldb/tools/lldb-server/lldb-gdbserver.cpp | 11 ++++++- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp index 32473b397baba..af8d230e7f5ce 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp @@ -15,6 +15,7 @@ #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h" #include "llvm/Support/Error.h" +#include #include #include #include @@ -35,8 +36,8 @@ static amd_dbgapi_status_t amd_dbgapi_client_process_get_info_callback( lldb::pid_t pid = debugger->GetNativeProcess()->GetID(); LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_client_process_get_info_callback callback, with query " - "%d, pid %d", - query, pid); + "%d, pid %lu", + query, (unsigned long)pid); switch (query) { case AMD_DBGAPI_CLIENT_PROCESS_INFO_OS_PID: { if (value_size != sizeof(amd_dbgapi_os_process_id_t)) @@ -56,7 +57,7 @@ static amd_dbgapi_status_t amd_dbgapi_insert_breakpoint_callback( amd_dbgapi_global_address_t address, amd_dbgapi_breakpoint_id_t breakpoint_id) { LLDB_LOGF(GetLog(GDBRLog::Plugin), - "insert_breakpoint callback at address: 0x%llx", address); + "insert_breakpoint callback at address: 0x%" PRIx64, address); LLDBServerPluginAMDGPU *debugger = reinterpret_cast(client_process_id); debugger->GetNativeProcess()->Halt(); @@ -74,7 +75,7 @@ static amd_dbgapi_status_t amd_dbgapi_insert_breakpoint_callback( static amd_dbgapi_status_t amd_dbgapi_remove_breakpoint_callback( amd_dbgapi_client_process_id_t client_process_id, amd_dbgapi_breakpoint_id_t breakpoint_id) { - LLDB_LOGF(GetLog(GDBRLog::Plugin), "remove_breakpoint callback for %llu", + LLDB_LOGF(GetLog(GDBRLog::Plugin), "remove_breakpoint callback for %" PRIu64, breakpoint_id.handle); return AMD_DBGAPI_STATUS_SUCCESS; } @@ -93,24 +94,24 @@ static amd_dbgapi_status_t amd_dbgapi_xfer_global_memory_callback( static void amd_dbgapi_log_message_callback(amd_dbgapi_log_level_t level, const char *message) { LLDB_LOGF(GetLog(GDBRLog::Plugin), "ROCdbgapi [%d]: %s", level, message); -}; +} static amd_dbgapi_callbacks_t s_dbgapi_callbacks = { - .allocate_memory = malloc, - .deallocate_memory = free, - .client_process_get_info = amd_dbgapi_client_process_get_info_callback, - .insert_breakpoint = amd_dbgapi_insert_breakpoint_callback, - .remove_breakpoint = amd_dbgapi_remove_breakpoint_callback, - .xfer_global_memory = amd_dbgapi_xfer_global_memory_callback, - .log_message = amd_dbgapi_log_message_callback, + malloc, + free, + amd_dbgapi_client_process_get_info_callback, + amd_dbgapi_insert_breakpoint_callback, + amd_dbgapi_remove_breakpoint_callback, + amd_dbgapi_xfer_global_memory_callback, + amd_dbgapi_log_message_callback, }; LLDBServerPluginAMDGPU::LLDBServerPluginAMDGPU( LLDBServerPlugin::GDBServer &native_process, MainLoop &main_loop) : LLDBServerPlugin(native_process, main_loop) { m_process_manager_up.reset(new ProcessManagerAMDGPU(main_loop)); - m_gdb_server.reset( - new GDBRemoteCommunicationServerLLGS(m_main_loop, *m_process_manager_up)); + m_gdb_server.reset(new GDBRemoteCommunicationServerLLGS( + m_main_loop, *m_process_manager_up, "amd-gpu.server")); } LLDBServerPluginAMDGPU::~LLDBServerPluginAMDGPU() { CloseFDs(); } @@ -415,7 +416,7 @@ std::optional LLDBServerPluginAMDGPU::NativeProcessIsStopping() { bool LLDBServerPluginAMDGPU::HandleGPUInternalBreakpointHit( const GPUInternalBreakpoinInfo &bp, bool &has_new_libraries) { LLDB_LOGF(GetLog(GDBRLog::Plugin), - "Hit GPU loader breakpoint at address: 0x%llx", bp.addr); + "Hit GPU loader breakpoint at address: 0x%" PRIx64, bp.addr); has_new_libraries = false; amd_dbgapi_breakpoint_id_t breakpoint_id{bp.breakpoind_id}; amd_dbgapi_breakpoint_action_t action; @@ -557,7 +558,7 @@ bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) { return SetGPUBreakpoint(addr, bp_instruction, bp_size); } -GPUPluginBreakpointHitResponse +llvm::Expected LLDBServerPluginAMDGPU::BreakpointWasHit(GPUPluginBreakpointHitArgs &args) { Log *log = GetLog(GDBRLog::Plugin); std::string json_string; diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h index e1f057e7f8956..79cb771319aca 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h @@ -94,7 +94,7 @@ class LLDBServerPluginAMDGPU : public LLDBServerPlugin { bool HandleEventFileDescriptorEvent(int fd) override; GPUActions GetInitializeActions() override; std::optional NativeProcessIsStopping() override; - GPUPluginBreakpointHitResponse + llvm::Expected BreakpointWasHit(GPUPluginBreakpointHitArgs &args) override; NativeProcessProtocol *GetNativeProcess() { diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp index df6015aabb9ee..af76f038e2117 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp @@ -17,6 +17,7 @@ #include "lldb/Utility/UnimplementedError.h" #include "llvm/Support/Error.h" +#include #include using namespace lldb; @@ -349,8 +350,9 @@ bool ProcessAMDGPU::handleWaveStop(amd_dbgapi_event_id_t eventId) { } LLDB_LOGF(GetLog(GDBRLog::Plugin), - "Wave stopped due to breakpoint at: 0x%llx with wave id: %llu " - "event id: %llu", + "Wave stopped due to breakpoint at: 0x%" PRIx64 + " with wave id: %" PRIu64 " " + "event id: %" PRIu64, pc, wave_id.handle, eventId.handle); return true; } else { @@ -383,14 +385,14 @@ static const char *event_kind_str(amd_dbgapi_event_kind_t kind) { case AMD_DBGAPI_EVENT_KIND_QUEUE_ERROR: return "QUEUE_ERROR"; } - assert(!"unhandled amd_dbgapi_event_kind_t value"); + assert(false && "unhandled amd_dbgapi_event_kind_t value"); } bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId, amd_dbgapi_event_kind_t eventKind) { - LLDB_LOGF(GetLog(GDBRLog::Plugin), "handleDebugEvent(%llu, %s)", + LLDB_LOGF(GetLog(GDBRLog::Plugin), "handleDebugEvent(%" PRIu64 ", %s)", eventId.handle, event_kind_str(eventKind)); - bool result; + bool result = false; if (eventKind == AMD_DBGAPI_EVENT_KIND_NONE) return result; @@ -413,10 +415,6 @@ bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId, case AMD_DBGAPI_RUNTIME_STATE_UNLOADED: LLDB_LOGF(GetLog(GDBRLog::Plugin), "Runtime unloaded"); break; - default: - LLDB_LOGF(GetLog(GDBRLog::Plugin), "Unknown runtime state: %d", - runtimeState); - break; } } @@ -490,8 +488,9 @@ bool ProcessAMDGPU::handleDebugEvent(amd_dbgapi_event_id_t eventId, if (status != AMD_DBGAPI_STATUS_SUCCESS) continue; - LLDB_LOGF(GetLog(GDBRLog::Plugin), "Code object %zu: %s at address %llu", - i, uri_bytes, l_addr); + LLDB_LOGF(GetLog(GDBRLog::Plugin), + "Code object %zu: %s at address %" PRIu64, i, uri_bytes, + l_addr); if (m_gpu_modules.find(l_addr) == m_gpu_modules.end()) { GPUModule mod = parseCodeObjectUrl(uri_bytes, l_addr); diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp index e34728b5ff85d..670a0eae75df2 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/RegisterContextAMDGPU.cpp @@ -33,6 +33,7 @@ static std::vector g_reg_sets; /// are accessed by the LLDB register numbers, which are defined above. static std::vector g_reg_infos; size_t g_register_buffer_size = 0; +static uint32_t s_gpu_pc_reg_num = 0; static std::unordered_map g_lldb_num_to_amd_reg_id; @@ -263,6 +264,7 @@ bool RegisterContextAMDGPU::InitRegisterInfos() { // Check if this is the PC register if (reg_id.handle == pc_register_id.handle) { reg_info.kinds[eRegisterKindGeneric] = LLDB_REGNUM_GENERIC_PC; + s_gpu_pc_reg_num = i; } // Add this register indices belong to its register classes @@ -508,10 +510,10 @@ std::vector RegisterContextAMDGPU::GetExpeditedRegisters(ExpeditedRegs expType) const { static std::vector g_expedited_regs; if (g_expedited_regs.empty()) { - // TODO: is this the correct way to do this? - // g_expedited_regs.push_back(LLDB_REGNUM_GENERIC_PC); - // g_expedited_regs.push_back(LLDB_SP); - // g_expedited_regs.push_back(LLDB_FP); + // We can't expedite all registers because that would cause jThreadsInfo to + // fetch registers from all stopped waves eagarly which would be too slow + // and unnecessary. + g_expedited_regs.push_back(s_gpu_pc_reg_num); } return g_expedited_regs; } diff --git a/lldb/tools/lldb-server/lldb-gdbserver.cpp b/lldb/tools/lldb-server/lldb-gdbserver.cpp index 11b17f05bb92c..e03b89ae20727 100644 --- a/lldb/tools/lldb-server/lldb-gdbserver.cpp +++ b/lldb/tools/lldb-server/lldb-gdbserver.cpp @@ -462,9 +462,18 @@ int main_gdbserver(int argc, char *argv[]) { GDBRemoteCommunicationServerLLGS gdb_server(mainloop, manager, "gdb-server"); #if defined(LLDB_ENABLE_AMDGPU_PLUGIN) || defined(LLDB_ENABLE_MOCKGPU_PLUGIN) +#if defined(LLDB_ENABLE_AMDGPU_PLUGIN) + // AMD GPU plugin requires to use the same mainloop as the native process. + // This is because AMD debug API has to be called from the same thread as the + // ptrace() thread. + MainLoop &gpu_mainloop = mainloop; +#else + // Any GPU plugins can use a separate mainloop. + MainLoop gpu_mainloop; +#endif // Install GPU plugin. gdb_server.InstallPlugin( - std::make_unique(gdb_server, mainloop)); + std::make_unique(gdb_server, gpu_mainloop)); #endif llvm::StringRef host_and_port; From 8dc56535d043907b20a335dc43f8a644c389129a Mon Sep 17 00:00:00 2001 From: Jeffrey Tan Date: Thu, 12 Jun 2025 11:12:10 -0700 Subject: [PATCH 3/3] Some refactoring --- lldb/tools/lldb-server/CMakeLists.txt | 2 +- .../lldb-server/Plugins/AMDGPU/CMakeLists.txt | 5 +- .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp | 20 +++----- .../Plugins/AMDGPU/LLDBServerPluginAMDGPU.h | 37 --------------- .../Plugins/AMDGPU/ProcessAMDGPU.cpp | 46 ------------------- .../Plugins/AMDGPU/ProcessAMDGPU.h | 3 -- 6 files changed, 9 insertions(+), 104 deletions(-) diff --git a/lldb/tools/lldb-server/CMakeLists.txt b/lldb/tools/lldb-server/CMakeLists.txt index 4126c02829441..ade88d8618539 100644 --- a/lldb/tools/lldb-server/CMakeLists.txt +++ b/lldb/tools/lldb-server/CMakeLists.txt @@ -42,7 +42,7 @@ if(APPLE_EMBEDDED) endif() endif() -if(DEFINED ROCM_PATH AND EXISTS ${ROCM_PATH}) +if(DEFINED ROCM_PATH) add_definitions(-DLLDB_ENABLE_AMDGPU_PLUGIN=1) list(APPEND LLDB_PLUGINS lldbServerPluginAMDGPU) else() diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt index f19d4a72aca9b..99493a3b03fb6 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/CMakeLists.txt @@ -1,9 +1,6 @@ -# Set ROCM paths -set(ROCM_PATH "" CACHE PATH "Path to ROCm installation") - message(STATUS "ROCM_PATH is set to: '${ROCM_PATH}'") -if(NOT ROCM_PATH OR ROCM_PATH STREQUAL "") +if(NOT ROCM_PATH) message(FATAL_ERROR "ROCM_PATH must be specified. Use -DROCM_PATH=/path/to/rocm") endif() diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp index af8d230e7f5ce..2bf34f73119b2 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.cpp @@ -496,6 +496,8 @@ bool LLDBServerPluginAMDGPU::SetGPUBreakpoint(uint64_t addr, bp.gpu_breakpoint_id = std::nullopt; // No GPU breakpoint ID for ptrace version + // TODO: use memory read/write API from native process instead of ptrace + // directly. auto pid = GetNativeProcess()->GetID(); // Read original bytes word by word std::vector original_words; @@ -522,19 +524,10 @@ bool LLDBServerPluginAMDGPU::SetGPUBreakpoint(uint64_t addr, } bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) { - // First get the architecture ID for this process - amd_dbgapi_architecture_id_t arch_id; - amd_dbgapi_status_t status = amd_dbgapi_get_architecture(0x02C, &arch_id); - if (status != AMD_DBGAPI_STATUS_SUCCESS) { - // Handle error - LLDB_LOGF(GetLog(GDBRLog::Plugin), "amd_dbgapi_get_architecture failed"); - return false; - } - // Get breakpoint instruction const uint8_t *bp_instruction; - status = amd_dbgapi_architecture_get_info( - arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION, + amd_dbgapi_status_t status = amd_dbgapi_architecture_get_info( + m_architecture_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION, sizeof(bp_instruction), &bp_instruction); if (status != AMD_DBGAPI_STATUS_SUCCESS) { LLDB_LOGF(GetLog(GDBRLog::Plugin), @@ -545,8 +538,9 @@ bool LLDBServerPluginAMDGPU::CreateGPUBreakpoint(uint64_t addr) { // Get breakpoint instruction size size_t bp_size; status = amd_dbgapi_architecture_get_info( - arch_id, AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE, - sizeof(bp_size), &bp_size); + m_architecture_id, + AMD_DBGAPI_ARCHITECTURE_INFO_BREAKPOINT_INSTRUCTION_SIZE, sizeof(bp_size), + &bp_size); if (status != AMD_DBGAPI_STATUS_SUCCESS) { LLDB_LOGF( GetLog(GDBRLog::Plugin), diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h index 79cb771319aca..7f657879b6ca4 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/LLDBServerPluginAMDGPU.h @@ -16,43 +16,6 @@ #include "ProcessAMDGPU.h" #include -// This is a mock GPU plugin that is used for testing the LLDBServerPlugin. It -// should be run with the following code as the main binary: -/* - -$ cat main.cpp -#include - -struct ShlibInfo { - const char *path = nullptr; - ShlibInfo *next = nullptr; -}; - -ShlibInfo g_shlib_list = { "/tmp/a.out", nullptr}; - -int gpu_initialize() { - return puts(__FUNCTION__); -} -int gpu_shlib_load() { - return puts(__FUNCTION__); -} -int main(int argc, const char **argv) { - gpu_initialize(); - gpu_shlib_load(); - return 0; // Break here -} - -$ clang++ -g -O0 -o a.out main.cpp -$ ninja lldb lldb-server -$ ./bin/lldb a.out -o 'b /Break here/ -o run - -*/ -// If the above code is run, you will be stopped at the breakpoint and the Mock -// GPU target will be selected. Try doing a "reg read --all" to see the state -// of the GPU registers. Then you can select the native process target with -// "target select 0" and issue commands to the native process, and then select -// the GPU target with "target select 1" and issue commands to the GPU target. - namespace lldb_private { class TCPSocket; diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp index af76f038e2117..22dae40ea2486 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.cpp @@ -104,8 +104,6 @@ const ArchSpec &ProcessAMDGPU::GetArchitecture() const { // Breakpoint functions Status ProcessAMDGPU::SetBreakpoint(lldb::addr_t addr, uint32_t size, bool hardware) { - // TODO: fix the race condition of GPU module load, client lldb setting - // breakpoint then resume GPU connection. bool success = m_debugger->CreateGPUBreakpoint(addr); if (!success) { return Status::FromErrorString("CreateGPUBreakpoint failed"); @@ -190,7 +188,6 @@ ProcessAMDGPU::GetGPUDynamicLoaderLibraryInfos( GPUDynamicLoaderResponse response; - // Access the GPU modules using the GetGPUModules() method const auto &gpu_modules = m_gpu_modules; LLDB_LOGF(log, "ProcessAMDGPU::%s() found %zu GPU modules", __FUNCTION__, @@ -235,49 +232,6 @@ ProcessAMDGPU::GetGPUDynamicLoaderLibraryInfos( return response; } -llvm::Expected> -ProcessAMDGPU::GetLoadedSVR4Libraries() { - std::vector libraries; - - // Check if we have a valid debugger instance - if (!m_debugger) { - return libraries; // Return empty vector if no debugger - } - - // Access the GPU modules using the GetGPUModules() method - const auto &gpu_modules = GetGPUModules(); - - // Convert each GPU module to an SVR4LibraryInfo object - for (const auto &[addr, module] : gpu_modules) { - if (module.is_loaded) { - SVR4LibraryInfo lib_info; - std::string path; - for (char c : module.path) { - if (c == '#') - path += "%23"; - else if (c == '$') - path += "%24"; - else if (c == '}') - path += "%7D"; - else if (c == '&') - path += "&"; - else - path += c; - } - lib_info.name = path; - lib_info.link_map = addr; - lib_info.base_addr = module.base_address; - lib_info.ld_addr = - module.size; // Using size as ld_addr as in handleLibrariesSvr4Read - lib_info.next = 0; // No next link in our implementation - - libraries.push_back(lib_info); - } - } - - return libraries; -} - llvm::Expected> ProcessManagerAMDGPU::Launch( ProcessLaunchInfo &launch_info, diff --git a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h index 08e8dd8352420..7c6d3956c3982 100644 --- a/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h +++ b/lldb/tools/lldb-server/Plugins/AMDGPU/ProcessAMDGPU.h @@ -82,9 +82,6 @@ class ProcessAMDGPU : public NativeProcessProtocol { // Custom accessors void SetLaunchInfo(ProcessLaunchInfo &launch_info); - llvm::Expected> - GetLoadedSVR4Libraries() override; - std::optional GetGPUDynamicLoaderLibraryInfos(const GPUDynamicLoaderArgs &args) override;