aboutsummaryrefslogtreecommitdiff
path: root/gdb/amd-dbgapi-target.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/amd-dbgapi-target.c')
-rw-r--r--gdb/amd-dbgapi-target.c277
1 files changed, 206 insertions, 71 deletions
diff --git a/gdb/amd-dbgapi-target.c b/gdb/amd-dbgapi-target.c
index 2bb79ac..e2a8ec8 100644
--- a/gdb/amd-dbgapi-target.c
+++ b/gdb/amd-dbgapi-target.c
@@ -1,6 +1,6 @@
/* Target used to communicate with the AMD Debugger API.
- Copyright (C) 2019-2024 Free Software Foundation, Inc.
+ Copyright (C) 2019-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -24,6 +24,8 @@
#include "cli/cli-cmds.h"
#include "cli/cli-decode.h"
#include "cli/cli-style.h"
+#include "gdbcore.h"
+#include "gdbsupport/unordered_map.h"
#include "inf-loop.h"
#include "inferior.h"
#include "objfiles.h"
@@ -207,13 +209,19 @@ struct amd_dbgapi_inferior_info
bool enabled = false;
} precise_memory;
- std::unordered_map<decltype (amd_dbgapi_breakpoint_id_t::handle),
+ gdb::unordered_map<decltype (amd_dbgapi_breakpoint_id_t::handle),
struct breakpoint *>
breakpoint_map;
/* List of pending events the amd-dbgapi target retrieved from the dbgapi. */
std::list<std::pair<ptid_t, target_waitstatus>> wave_events;
+ /* Map of threads with ongoing displaced steps to corresponding amd-dbgapi
+ displaced stepping handles. */
+ gdb::unordered_map<thread_info *,
+ decltype (amd_dbgapi_displaced_stepping_id_t::handle)>
+ stepping_id_map;
+
/* Map of wave ID to wave_info. We cache wave_info objects because
we need to access the info after the wave is gone, in the thread
exit nofication. E.g.:
@@ -221,12 +229,12 @@ struct amd_dbgapi_inferior_info
wave_info objects are added when we first see the wave, and
removed from a thread_deleted observer. */
- std::unordered_map<decltype (amd_dbgapi_wave_id_t::handle), wave_info>
+ gdb::unordered_map<decltype (amd_dbgapi_wave_id_t::handle), wave_info>
wave_info_map;
};
static amd_dbgapi_event_id_t process_event_queue
- (amd_dbgapi_process_id_t process_id,
+ (amd_dbgapi_inferior_info &info,
amd_dbgapi_event_kind_t until_event_kind = AMD_DBGAPI_EVENT_KIND_NONE);
static const target_info amd_dbgapi_target_info = {
@@ -290,6 +298,21 @@ struct amd_dbgapi_target final : public target_ops
bool stopped_by_sw_breakpoint () override;
bool stopped_by_hw_breakpoint () override;
+ bool supports_displaced_step (thread_info *thread) override
+ {
+ /* Handle displaced stepping for GPU threads only. */
+ if (!ptid_is_gpu (thread->ptid))
+ return beneath ()->supports_displaced_step (thread);
+
+ return true;
+ }
+
+ displaced_step_prepare_status displaced_step_prepare
+ (thread_info *thread, CORE_ADDR &displaced_pc) override;
+
+ displaced_step_finish_status displaced_step_finish
+ (thread_info *thread, const target_waitstatus &status) override;
+
private:
/* True if we must report thread events. */
bool m_report_thread_events = false;
@@ -420,6 +443,32 @@ async_event_handler_mark ()
mark_async_event_handler (amd_dbgapi_async_event_handler);
}
+/* Set forward progress requirement to REQUIRE for inferior INFO. */
+
+static void
+require_forward_progress (amd_dbgapi_inferior_info &info, bool require)
+{
+ /* If we try to disable forward progress requirement but the target expects
+ resumed threads to be committed to the target, we could wait for events
+ that will never arrive. */
+ if (!require)
+ gdb_assert (!info.inf->process_target ()->commit_resumed_state);
+
+ gdb_assert (info.process_id != AMD_DBGAPI_PROCESS_NONE);
+
+ /* Don't do unnecessary calls to amd-dbgapi to avoid polluting the logs. */
+ if (info.forward_progress_required == require)
+ return;
+
+ const auto progress
+ = require ? AMD_DBGAPI_PROGRESS_NORMAL : AMD_DBGAPI_PROGRESS_NO_FORWARD;
+ const auto status
+ = amd_dbgapi_process_set_progress (info.process_id, progress);
+ gdb_assert (status == AMD_DBGAPI_STATUS_SUCCESS);
+
+ info.forward_progress_required = require;
+}
+
/* Set forward progress requirement to REQUIRE for all processes of PROC_TARGET
matching PTID. */
@@ -434,21 +483,8 @@ require_forward_progress (ptid_t ptid, process_stratum_target *proc_target,
amd_dbgapi_inferior_info *info = get_amd_dbgapi_inferior_info (inf);
- if (info->process_id == AMD_DBGAPI_PROCESS_NONE)
- continue;
-
- /* Don't do unnecessary calls to amd-dbgapi to avoid polluting the logs. */
- if (info->forward_progress_required == require)
- continue;
-
- amd_dbgapi_status_t status
- = amd_dbgapi_process_set_progress
- (info->process_id, (require
- ? AMD_DBGAPI_PROGRESS_NORMAL
- : AMD_DBGAPI_PROGRESS_NO_FORWARD));
- gdb_assert (status == AMD_DBGAPI_STATUS_SUCCESS);
-
- info->forward_progress_required = require;
+ if (info->process_id != AMD_DBGAPI_PROCESS_NONE)
+ require_forward_progress (*info, require);
/* If ptid targets a single inferior and we have found it, no need to
continue. */
@@ -532,11 +568,12 @@ amd_dbgapi_target_breakpoint::check_status (struct bpstat *bs)
if (action == AMD_DBGAPI_BREAKPOINT_ACTION_RESUME)
return;
+ require_forward_progress (*info, false);
+
/* If the action is AMD_DBGAPI_BREAKPOINT_ACTION_HALT, we need to wait until
a breakpoint resume event for this breakpoint_id is seen. */
amd_dbgapi_event_id_t resume_event_id
- = process_event_queue (info->process_id,
- AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME);
+ = process_event_queue (*info, AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME);
/* We should always get a breakpoint_resume event after processing all
events generated by reporting the breakpoint hit. */
@@ -1115,32 +1152,14 @@ add_gpu_thread (inferior *inf, ptid_t wave_ptid)
/* Process an event that was just pulled out of the amd-dbgapi library. */
static void
-process_one_event (amd_dbgapi_event_id_t event_id,
+process_one_event (amd_dbgapi_inferior_info &info,
+ amd_dbgapi_event_id_t event_id,
amd_dbgapi_event_kind_t event_kind)
{
/* Automatically mark this event processed when going out of scope. */
scoped_amd_dbgapi_event_processed mark_event_processed (event_id);
- amd_dbgapi_process_id_t process_id;
- amd_dbgapi_status_t status
- = amd_dbgapi_event_get_info (event_id, AMD_DBGAPI_EVENT_INFO_PROCESS,
- sizeof (process_id), &process_id);
- if (status != AMD_DBGAPI_STATUS_SUCCESS)
- error (_("event_get_info for event_%ld failed (%s)"), event_id.handle,
- get_status_string (status));
-
- amd_dbgapi_os_process_id_t pid;
- status = amd_dbgapi_process_get_info (process_id,
- AMD_DBGAPI_PROCESS_INFO_OS_ID,
- sizeof (pid), &pid);
- if (status != AMD_DBGAPI_STATUS_SUCCESS)
- error (_("process_get_info for process_%ld failed (%s)"),
- process_id.handle, get_status_string (status));
-
- auto *proc_target = current_inferior ()->process_target ();
- inferior *inf = find_inferior_pid (proc_target, pid);
- gdb_assert (inf != nullptr);
- amd_dbgapi_inferior_info *info = get_amd_dbgapi_inferior_info (inf);
+ gdb_assert (info.inf != nullptr);
switch (event_kind)
{
@@ -1148,14 +1167,14 @@ process_one_event (amd_dbgapi_event_id_t event_id,
case AMD_DBGAPI_EVENT_KIND_WAVE_STOP:
{
amd_dbgapi_wave_id_t wave_id;
- status
+ amd_dbgapi_status_t status
= amd_dbgapi_event_get_info (event_id, AMD_DBGAPI_EVENT_INFO_WAVE,
sizeof (wave_id), &wave_id);
if (status != AMD_DBGAPI_STATUS_SUCCESS)
error (_("event_get_info for event_%ld failed (%s)"),
event_id.handle, get_status_string (status));
- ptid_t event_ptid = make_gpu_ptid (pid, wave_id);
+ ptid_t event_ptid = make_gpu_ptid (info.inf->pid, wave_id);
target_waitstatus ws;
amd_dbgapi_wave_stop_reasons_t stop_reason;
@@ -1196,9 +1215,10 @@ process_one_event (amd_dbgapi_event_id_t event_id,
else
ws.set_stopped (GDB_SIGNAL_0);
- thread_info *thread = proc_target->find_thread (event_ptid);
+ thread_info *thread
+ = info.inf->process_target ()->find_thread (event_ptid);
if (thread == nullptr)
- thread = add_gpu_thread (inf, event_ptid);
+ thread = add_gpu_thread (info.inf, event_ptid);
/* If the wave is stopped because of a software breakpoint, the
program counter needs to be adjusted so that it points to the
@@ -1220,7 +1240,7 @@ process_one_event (amd_dbgapi_event_id_t event_id,
error (_("wave_get_info for wave_%ld failed (%s)"),
wave_id.handle, get_status_string (status));
- info->wave_events.emplace_back (event_ptid, ws);
+ info.wave_events.emplace_back (event_ptid, ws);
break;
}
@@ -1238,7 +1258,7 @@ process_one_event (amd_dbgapi_event_id_t event_id,
When amd_dbgapi_target_breakpoint::check_status is called, the current
inferior is the inferior that hit the breakpoint, which should still be
the case now. */
- gdb_assert (inf == current_inferior ());
+ gdb_assert (info.inf == current_inferior ());
handle_solib_event ();
break;
@@ -1252,22 +1272,22 @@ process_one_event (amd_dbgapi_event_id_t event_id,
{
amd_dbgapi_runtime_state_t runtime_state;
- status = amd_dbgapi_event_get_info (event_id,
- AMD_DBGAPI_EVENT_INFO_RUNTIME_STATE,
- sizeof (runtime_state),
- &runtime_state);
+ amd_dbgapi_status_t status
+ = amd_dbgapi_event_get_info (event_id,
+ AMD_DBGAPI_EVENT_INFO_RUNTIME_STATE,
+ sizeof (runtime_state), &runtime_state);
if (status != AMD_DBGAPI_STATUS_SUCCESS)
error (_("event_get_info for event_%ld failed (%s)"),
event_id.handle, get_status_string (status));
gdb_assert (runtime_state == AMD_DBGAPI_RUNTIME_STATE_UNLOADED);
gdb_assert
- (info->runtime_state == AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS);
+ (info.runtime_state == AMD_DBGAPI_RUNTIME_STATE_LOADED_SUCCESS);
- info->runtime_state = runtime_state;
+ info.runtime_state = runtime_state;
- gdb_assert (inf->target_is_pushed (&the_amd_dbgapi_target));
- inf->unpush_target (&the_amd_dbgapi_target);
+ gdb_assert (info.inf->target_is_pushed (&the_amd_dbgapi_target));
+ info.inf->unpush_target (&the_amd_dbgapi_target);
}
break;
@@ -1308,20 +1328,18 @@ event_kind_str (amd_dbgapi_event_kind_t kind)
gdb_assert_not_reached ("unhandled amd_dbgapi_event_kind_t value");
}
-/* Drain the dbgapi event queue of a given process_id, or of all processes if
- process_id is AMD_DBGAPI_PROCESS_NONE. Stop processing the events if an
- event of a given kind is requested and `process_id` is not
- AMD_DBGAPI_PROCESS_NONE. Wave stop events that are not returned are queued
- into their inferior's amd_dbgapi_inferior_info pending wave events. */
+/* Drain the dbgapi event queue of a given inferior. Stop processing the
+ events if an event of a given kind is requested (not AMD_DBGAPI_EVENT_NONE).
+ Wave stop events that are not returned are queued into their inferior's
+ amd_dbgapi_inferior_info pending wave events. */
static amd_dbgapi_event_id_t
-process_event_queue (amd_dbgapi_process_id_t process_id,
+process_event_queue (amd_dbgapi_inferior_info &info,
amd_dbgapi_event_kind_t until_event_kind)
{
- /* An event of a given type can only be requested from a single
- process_id. */
- gdb_assert (until_event_kind == AMD_DBGAPI_EVENT_KIND_NONE
- || process_id != AMD_DBGAPI_PROCESS_NONE);
+ /* Pulling events with forward progress required may result in bad
+ performance, make sure it is not required. */
+ gdb_assert (!info.forward_progress_required);
while (true)
{
@@ -1329,7 +1347,7 @@ process_event_queue (amd_dbgapi_process_id_t process_id,
amd_dbgapi_event_kind_t event_kind;
amd_dbgapi_status_t status
- = amd_dbgapi_process_next_pending_event (process_id, &event_id,
+ = amd_dbgapi_process_next_pending_event (info.process_id, &event_id,
&event_kind);
if (status != AMD_DBGAPI_STATUS_SUCCESS)
@@ -1345,7 +1363,7 @@ process_event_queue (amd_dbgapi_process_id_t process_id,
if (event_id == AMD_DBGAPI_EVENT_NONE || event_kind == until_event_kind)
return event_id;
- process_one_event (event_id, event_kind);
+ process_one_event (info, event_id, event_kind);
}
}
@@ -1450,7 +1468,7 @@ amd_dbgapi_target::wait (ptid_t ptid, struct target_waitstatus *ws,
/* Drain the events for the current inferior from the amd_dbgapi and
preserve the ordering. */
auto info = get_amd_dbgapi_inferior_info (current_inferior ());
- process_event_queue (info->process_id, AMD_DBGAPI_EVENT_KIND_NONE);
+ process_event_queue (*info);
std::tie (event_ptid, gpu_waitstatus) = consume_one_event (ptid.pid ());
if (event_ptid == minus_one_ptid)
@@ -1837,13 +1855,14 @@ amd_dbgapi_target::update_thread_list ()
if (changed == AMD_DBGAPI_CHANGED_NO)
continue;
+ gdb::unique_xmalloc_ptr<amd_dbgapi_wave_id_t> wave_list_holder
+ (wave_list);
+
/* Create a set and free the wave list. */
std::set<ptid_t::tid_type> threads;
for (size_t i = 0; i < count; ++i)
threads.emplace (wave_list[i].handle);
- xfree (wave_list);
-
/* Prune the wave_ids that already have a thread_info. Any thread_info
which does not have a corresponding wave_id represents a wave which
is gone at this point and should be deleted. */
@@ -1896,6 +1915,122 @@ amd_dbgapi_target::update_thread_list ()
this->beneath ()->update_thread_list ();
}
+displaced_step_prepare_status
+amd_dbgapi_target::displaced_step_prepare (thread_info *thread,
+ CORE_ADDR &displaced_pc)
+{
+ if (!ptid_is_gpu (thread->ptid))
+ return beneath ()->displaced_step_prepare (thread, displaced_pc);
+
+ gdb_assert (!thread->displaced_step_state.in_progress ());
+
+ /* Read the bytes that were overwritten by the breakpoint instruction being
+ stepped over. */
+ CORE_ADDR original_pc = regcache_read_pc (get_thread_regcache (thread));
+ gdbarch *arch = get_thread_regcache (thread)->arch ();
+ size_t size = get_amdgpu_gdbarch_tdep (arch)->breakpoint_instruction_size;
+ gdb::byte_vector overwritten_bytes (size);
+
+ read_memory (original_pc, overwritten_bytes.data (), size);
+
+ /* Ask dbgapi to start the displaced step. */
+ amd_dbgapi_wave_id_t wave_id = get_amd_dbgapi_wave_id (thread->ptid);
+ amd_dbgapi_displaced_stepping_id_t stepping_id;
+ amd_dbgapi_status_t status
+ = amd_dbgapi_displaced_stepping_start (wave_id, overwritten_bytes.data (),
+ &stepping_id);
+
+ switch (status)
+ {
+ case AMD_DBGAPI_STATUS_SUCCESS:
+ break;
+
+ case AMD_DBGAPI_STATUS_ERROR_DISPLACED_STEPPING_BUFFER_NOT_AVAILABLE:
+ return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
+
+ case AMD_DBGAPI_STATUS_ERROR_ILLEGAL_INSTRUCTION:
+ return DISPLACED_STEP_PREPARE_STATUS_CANT;
+
+ default:
+ error (_("amd_dbgapi_displaced_stepping_start failed (%s)"),
+ get_status_string (status));
+ }
+
+ /* Save the displaced stepping id in the per-inferior info. */
+ amd_dbgapi_inferior_info *info = get_amd_dbgapi_inferior_info (thread->inf);
+
+ bool inserted
+ = info->stepping_id_map.emplace (thread, stepping_id.handle).second;
+ gdb_assert (inserted);
+
+ /* Get the new (displaced) PC. */
+ status = amd_dbgapi_wave_get_info (wave_id, AMD_DBGAPI_WAVE_INFO_PC,
+ sizeof (displaced_pc), &displaced_pc);
+ if (status != AMD_DBGAPI_STATUS_SUCCESS)
+ {
+ amd_dbgapi_displaced_stepping_complete (wave_id, stepping_id);
+ error (_("amd_dbgapi_wave_get_info failed (%s), could not get the "
+ "thread's displaced PC."),
+ get_status_string (status));
+ }
+
+ displaced_debug_printf ("selected buffer at %#lx", displaced_pc);
+
+ /* We may have written some registers, so flush the register cache. */
+ registers_changed_thread (thread);
+
+ return DISPLACED_STEP_PREPARE_STATUS_OK;
+}
+
+displaced_step_finish_status
+amd_dbgapi_target::displaced_step_finish (thread_info *thread,
+ const target_waitstatus &ws)
+{
+ if (!ptid_is_gpu (thread->ptid))
+ return beneath ()->displaced_step_finish (thread, ws);
+
+ gdb_assert (thread->displaced_step_state.in_progress ());
+
+ /* Find the displaced stepping id for this thread. */
+ amd_dbgapi_inferior_info *info = get_amd_dbgapi_inferior_info (thread->inf);
+ auto entry = info->stepping_id_map.extract (thread);
+
+ gdb_assert (entry.has_value ());
+ amd_dbgapi_displaced_stepping_id_t stepping_id {entry->second};
+
+ /* If the thread exited while stepping, we are done. The code above
+ cleared our associated resources. We don't want to call dbgapi
+ below: since the thread is gone, we wouldn't be able to find the
+ necessary wave ID. dbgapi already took care of releasing its
+ displaced-stepping-related resources when it deleted the
+ wave. */
+ if (ws.kind () == TARGET_WAITKIND_THREAD_EXITED)
+ return DISPLACED_STEP_FINISH_STATUS_OK;
+
+ amd_dbgapi_wave_id_t wave_id = get_amd_dbgapi_wave_id (thread->ptid);
+ amd_dbgapi_wave_stop_reasons_t stop_reason;
+ amd_dbgapi_status_t 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)
+ error (_("wave_get_info for wave_%ld failed (%s)"), wave_id.handle,
+ get_status_string (status));
+
+ status = amd_dbgapi_displaced_stepping_complete (wave_id, stepping_id);
+
+ if (status != AMD_DBGAPI_STATUS_SUCCESS)
+ error (_("amd_dbgapi_displaced_stepping_complete failed (%s)"),
+ get_status_string (status));
+
+ /* We may have written some registers, so flush the register cache. */
+ registers_changed_thread (thread);
+
+ return (stop_reason & AMD_DBGAPI_WAVE_STOP_REASON_SINGLE_STEP) != 0
+ ? DISPLACED_STEP_FINISH_STATUS_OK
+ : DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
+}
+
/* inferior_created observer. */
static void