From: Lancelot SIX <lancelot.six@amd.com>
To: Bratislav Filipovic <bfilipov@amd.com>
Cc: <simon.marchi@polymtl.ca>, <gdb-patches@sourceware.org>
Subject: Re: [PATCH v3] gdb: Implement stop-on-solib-events for GPU code objects
Date: Mon, 12 Oct 2026 15:32:36 +0100 [thread overview]
Message-ID: <2j2c6ytzi7ooevyncfwj6siff45zs3loivxc6siuy2tqnjxzrb@logfn6xqblyw> (raw)
In-Reply-To: <20261008130956.55573-1-bfilipov@amd.com>
Hi,
Thanks for the work you are doing on this.
On Thu, Oct 08, 2026 at 03:09:42PM +0200, Bratislav Filipovic wrote:
> GDB's "set stop-on-solib-events" setting allows users to stop execution
> when shared libraries are loaded or unloaded, enabling inspection and
> breakpoint placement before library code executes. This feature works for
> CPU shared libraries but does not work for GPU code objects loaded by the
> AMD ROCm runtime.
>
> This commit implements stop-on-solib-events support for GPU code objects,
> making GPU code object load/unload events behave consistently with CPU
> shared library events.
>
> The root cause is in amd_dbgapi_target_breakpoint::check_status(), which
> unconditionally sets bs->stop = 0, regardless of the stop_on_solib_events
> setting. This is in contrast to internal_breakpoint::check_status() for CPU
> shared libraries, which respects the setting.
>
> The fix includes:
>
> 1. Change stop_on_solib_events from 'int' to 'bool' type throughout the
> codebase. Update add_setshow_zinteger_cmd to add_setshow_boolean_cmd,
> change help text from "If nonzero" to "If enabled", and remove != 0
> comparisons. The command now accepts on/off values while maintaining
> backwards compatibility with 0/1.
>
> 2. Refactor print_solib_event() to use a struct-based design that
> centralizes messages and eliminates code duplication between CPU and GPU
> event printing. The function now takes a print_solib_event_strings
> struct containing translatable message strings and the MI object-kind
> field value. This makes it easy to support different types of loadable
> objects without repeating GPU code object strings in multiple places.
>
> 3. Modify check_status() to track when CODE_OBJECT_LIST_UPDATED events
> occur during process_event_queue() and update bs->stop and bs->print
> based on stop_on_solib_events setting. The bs->print_it field is left
> at its default value (print_it_normal), relying on GDB's automatic logic
> to set it to print_it_noop when appropriate. This matches the CPU
> internal_breakpoint pattern and eliminates redundant assignments.
>
> 4. Implement print_it() override to display "Stopped due to GPU code object
> event" message using the new struct-based print_solib_event design.
>
> 5. Add MI changes to report object-kind field for all solib events, with
> values "shared-library" for CPU libraries and "gpu-code-object" for GPU
> code objects. The "library" field name is unchanged for backwards
> compatibility.
>
I am not sure I fully agree we should have different kinds here. At the
moment, GPU code objects are reported as any other shared library. When
doing `info sharedlibrary`, the GPU code objects are listed the same way
as host libraries.
Introducing different messages and notifications start to introduce a
divergence. I feel like if we wanted to introduce a difference (by
solib provider / kind / whatever), this should imply changes beyond
print_solib_event to remain consistent.
Those are just my thoughts, happy to hear other views.
> 6. Add comprehensive test coverage in gdb.rocm/solib-event.exp testing both
> file:// events (hipModuleLoad) and memory:// events (hipModuleLoadData),
> as well as unload events. The test includes both CLI and MI output
> verification. MI tests use manual gdb_expect patterns to avoid triggering
> a duplicate notification bug in vanilla GDB's mi_expect_stop.
>
> When enabled, GDB now stops on GPU code object load/unload events with
> "Stopped due to GPU code object event" messages, allowing users to inspect
> and debug GPU code object lifecycle consistently with CPU shared libraries.
>
> ---
> Testing notes:
> - All 18 tests pass (9 CLI + 9 MI tests) on AMD ROCm systems with gfx90a GPU
> - Empirically verified that GPU code object unload events fire automatically
> on process termination even without explicit hipModuleUnload calls,
> matching CPU shared library cleanup behavior.
> - MI tests use manual gdb_expect patterns instead of mi_expect_stop due to
> a bug where mi_expect_stop triggers duplicate MI notifications.
> - Updated mi-support.exp to accept optional object-kind field in MI
> solib-event output for backward compatibility with existing MI consumers.
>
> gdb/NEWS | 13 +
> gdb/amd-dbgapi-target.c | 55 +++-
> gdb/break-catch-load.c | 2 +-
> gdb/breakpoint.c | 44 +++-
> gdb/breakpoint.h | 28 ++-
> gdb/doc/gdb.texinfo | 13 +-
> gdb/infrun.c | 11 +-
> gdb/testsuite/gdb.rocm/solib-event-kernel.cpp | 27 ++
> gdb/testsuite/gdb.rocm/solib-event.cpp | 105 ++++++++
> gdb/testsuite/gdb.rocm/solib-event.exp | 234 ++++++++++++++++++
> gdb/testsuite/lib/mi-support.exp | 2 +-
> 11 files changed, 503 insertions(+), 31 deletions(-)
> create mode 100644 gdb/testsuite/gdb.rocm/solib-event-kernel.cpp
> create mode 100644 gdb/testsuite/gdb.rocm/solib-event.cpp
> create mode 100644 gdb/testsuite/gdb.rocm/solib-event.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 5d0c4745653..9cd597636d5 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,11 @@
>
> *** Changes since GDB 18
>
> +* The 'set stop-on-solib-events' setting now applies to GPU code objects
> + loaded by the AMD ROCm runtime, in addition to CPU shared libraries.
> + When enabled, GDB will stop execution whenever GPU code objects are
> + loaded or unloaded, allowing inspection and breakpoint placement.
> +
> * GDB now distinguishes between the GNU (MinGW) and MSVC Windows ABIs.
>
> The "set osabi" command accepts two new values, "Windows-GNU" and
> @@ -79,6 +84,14 @@ info locals
> The new "-shadowed on|off" option overrides the "set print shadowed"
> setting for a single invocation of the command.
>
> +* MI changes
> +
> +** The solib-event notification now includes an 'object-kind' field
> + to distinguish different types of loadable objects. Currently
> + defined values are 'shared-library' for CPU shared libraries and
> + 'gpu-code-object' for GPU code objects. For backwards compatibility,
> + the 'library' field name is used for both types of objects.
> +
> *** Changes in GDB 18
>
> * Support for the Common Trace Format (CTF) has been removed. GDB now
> diff --git a/gdb/amd-dbgapi-target.c b/gdb/amd-dbgapi-target.c
> index 93207a8fdd2..64e9a3aee94 100644
> --- a/gdb/amd-dbgapi-target.c
> +++ b/gdb/amd-dbgapi-target.c
> @@ -30,11 +30,13 @@
> #include "gdbsupport/unordered_map.h"
> #include "inf-loop.h"
> #include "inferior.h"
> +#include "infrun.h"
> #include "objfiles.h"
> #include "observable.h"
> #include "registry.h"
> #include "solib.h"
> #include "target.h"
> +#include "ui-out.h"
>
> #include <map>
>
> @@ -275,7 +277,17 @@ struct amd_dbgapi_inferior_info
> wave_info_map;
> };
>
> -static amd_dbgapi_event_id_t process_event_queue
> +/* Return value for process_event_queue. */
> +struct process_event_queue_result
> +{
> + /* The event ID, or AMD_DBGAPI_EVENT_NONE if no event was found. */
> + amd_dbgapi_event_id_t event_id;
> +
> + /* True if a CODE_OBJECT_LIST_UPDATED event was seen during processing. */
> + bool code_object_list_updated;
> +};
> +
> +static process_event_queue_result process_event_queue
> (amd_dbgapi_inferior_info &info,
> amd_dbgapi_event_kind_t until_event_kind = AMD_DBGAPI_EVENT_KIND_NONE);
>
> @@ -587,6 +599,7 @@ struct amd_dbgapi_target_breakpoint : public code_breakpoint
>
> void re_set (program_space *) override;
> void check_status (struct bpstat *bs) override;
> + enum print_stop_action print_it (const bpstat *bs) const override;
> };
>
> void
> @@ -603,7 +616,6 @@ amd_dbgapi_target_breakpoint::check_status (struct bpstat *bs)
> amd_dbgapi_status_t status;
>
> bs->stop = 0;
> - bs->print_it = print_it_noop;
>
> /* Find the address the breakpoint is set at. */
> auto match_breakpoint
> @@ -639,7 +651,7 @@ amd_dbgapi_target_breakpoint::check_status (struct bpstat *bs)
>
> /* 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
> + auto [resume_event_id, code_object_list_updated]
> = process_event_queue (info, AMD_DBGAPI_EVENT_KIND_BREAKPOINT_RESUME);
>
> /* We should always get a breakpoint_resume event after processing all
> @@ -665,6 +677,26 @@ amd_dbgapi_target_breakpoint::check_status (struct bpstat *bs)
> pulongest (resume_breakpoint_id.handle));
>
> amd_dbgapi_event_processed (resume_event_id);
> +
> + /* If a CODE_OBJECT_LIST_UPDATED event was seen during event processing,
> + check if the user requested to stop on solib events. This implements
> + stop-on-solib-events for GPU code objects. */
> + if (code_object_list_updated && stop_on_solib_events)
> + {
> + bs->stop = true;
> + bs->print = true;
> + }
> +}
> +
> +enum print_stop_action
> +amd_dbgapi_target_breakpoint::print_it (const bpstat *bs) const
> +{
> + /* We only reach here when check_status set bs->print_it to print_it_normal,
> + which happens only for GPU code object events when stop_on_solib_events
> + is enabled. */
> + print_solib_event (false, print_solib_event_strings_gpu_code_object);
> +
> + return PRINT_NOTHING;
> }
>
> bool
> @@ -1460,7 +1492,8 @@ add_gpu_thread (inferior *inf, ptid_t wave_ptid)
> static void
> process_one_event (amd_dbgapi_inferior_info &info,
> amd_dbgapi_event_id_t event_id,
> - amd_dbgapi_event_kind_t event_kind)
> + amd_dbgapi_event_kind_t event_kind,
> + bool &code_object_list_updated)
> {
> /* Automatically mark this event processed when going out of scope. */
> scoped_amd_dbgapi_event_processed mark_event_processed (event_id);
> @@ -1565,6 +1598,7 @@ process_one_event (amd_dbgapi_inferior_info &info,
> inferior is the inferior that hit the breakpoint, which should still be
> the case now. */
> gdb_assert (info.inf == current_inferior ());
> + code_object_list_updated = true;
> handle_solib_event ();
> break;
>
> @@ -1639,7 +1673,7 @@ event_kind_str (amd_dbgapi_event_kind_t kind)
> 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
> +static process_event_queue_result
> process_event_queue (amd_dbgapi_inferior_info &info,
> amd_dbgapi_event_kind_t until_event_kind)
> {
> @@ -1647,6 +1681,8 @@ process_event_queue (amd_dbgapi_inferior_info &info,
> performance, make sure it is not required. */
> gdb_assert (!info.forward_progress_required);
>
> + bool code_object_list_updated = false;
> +
> while (true)
> {
> amd_dbgapi_event_id_t event_id;
> @@ -1667,9 +1703,9 @@ process_event_queue (amd_dbgapi_inferior_info &info,
> event_kind_str (event_kind));
>
> if (event_id == AMD_DBGAPI_EVENT_NONE || event_kind == until_event_kind)
> - return event_id;
> + return {event_id, code_object_list_updated};
>
> - process_one_event (info, event_id, event_kind);
> + process_one_event (info, event_id, event_kind, code_object_list_updated);
> }
> }
>
> @@ -1809,6 +1845,11 @@ amd_dbgapi_target::wait (ptid_t ptid, struct target_waitstatus *ws,
> }
> }
>
> + /* We are returning a real stop event, so don't mark the async handler.
> + This prevents the event loop from re-processing the same stop event,
> + which would cause duplicate MI notifications. */
> + more_events.release ();
> +
This bit seems odd. We purposefully mark the event handler so
amd_dbgapi_target::wait gets called until it returns minus_one_ptid.
Doing this release could mean that we still have events pending
(either in wave_events, or down in dbgapi / the driver), but GDB's core
might not know and wait to be notified, which might not happen.
Best,
Lancelot.
> *ws = gpu_waitstatus;
> return event_ptid;
> }
> diff --git a/gdb/break-catch-load.c b/gdb/break-catch-load.c
> index 5492a56b423..b4e0a2978cb 100644
> --- a/gdb/break-catch-load.c
> +++ b/gdb/break-catch-load.c
> @@ -151,7 +151,7 @@ solib_catchpoint::print_it (const bpstat *bs) const
> uiout->text ("\n");
> if (uiout->is_mi_like_p ())
> uiout->field_string ("disp", bpdisp_text (this->disposition));
> - print_solib_event (true);
> + print_solib_event (true, print_solib_event_strings_shared_library);
> return PRINT_SRC_AND_LOC;
> }
>
> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index 0f5360c4ce5..e2c6614b68d 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -5368,24 +5368,44 @@ print_bp_stop_message (bpstat *bs)
>
> /* See breakpoint.h. */
>
> +const print_solib_event_strings print_solib_event_strings_shared_library =
> +{
> + N_("Stopped due to shared library event:\n"),
> + N_("Stopped due to shared library event (no libraries added or "
> + "removed)\n"),
> + "shared-library"
> +};
> +
> +/* See breakpoint.h. */
> +
> +const print_solib_event_strings print_solib_event_strings_gpu_code_object =
> +{
> + N_("Stopped due to GPU code object event:\n"),
> + N_("Stopped due to GPU code object event (no GPU code objects added or "
> + "removed)\n"),
> + "gpu-code-object"
> +};
> +
> +/* See breakpoint.h. */
> +
> void
> -print_solib_event (bool is_catchpoint)
> +print_solib_event (bool is_catchpoint,
> + const print_solib_event_strings &strings)
> {
> bool any_deleted = !current_program_space->deleted_solibs.empty ();
> bool any_added = !current_program_space->added_solibs.empty ();
>
> if (!is_catchpoint)
> - {
> - if (any_added || any_deleted)
> - current_uiout->text (_("Stopped due to shared library event:\n"));
> - else
> - current_uiout->text (_("Stopped due to shared library event (no "
> - "libraries added or removed)\n"));
> - }
> + current_uiout->text (any_added || any_deleted
> + ? _(strings.stopped_msg)
> + : _(strings.stopped_no_change_msg));
>
> if (current_uiout->is_mi_like_p ())
> - current_uiout->field_string ("reason",
> - async_reason_lookup (EXEC_ASYNC_SOLIB_EVENT));
> + {
> + current_uiout->field_string
> + ("reason", async_reason_lookup (EXEC_ASYNC_SOLIB_EVENT));
> + current_uiout->field_string ("object-kind", strings.object_kind);
> + }
>
> if (any_deleted)
> {
> @@ -5462,7 +5482,7 @@ bpstat_print (bpstat *bs, target_waitkind kind)
> OS-level shared library event, do the same thing. */
> if (kind == TARGET_WAITKIND_LOADED)
> {
> - print_solib_event (false);
> + print_solib_event (false, print_solib_event_strings_shared_library);
> return PRINT_NOTHING;
> }
>
> @@ -12547,7 +12567,7 @@ internal_breakpoint::print_it (const bpstat *bs) const
> /* Did we stop because the user set the stop_on_solib_events
> variable? (If so, we report this as a generic, "Stopped due
> to shlib event" message.) */
> - print_solib_event (false);
> + print_solib_event (false, print_solib_event_strings_shared_library);
> break;
>
> case bp_thread_event:
> diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
> index ba6eb15a47e..5cc5d814a54 100644
> --- a/gdb/breakpoint.h
> +++ b/gdb/breakpoint.h
> @@ -2098,11 +2098,37 @@ extern void catch_exception_event (enum exception_event_kind ex_event,
> const char *regex, bool tempflag,
> int from_tty);
>
> +/* Strings used by print_solib_event to describe a kind of loadable
> + object. */
> +
> +struct print_solib_event_strings
> +{
> + /* Message printed when objects were added or removed. */
> + const char *stopped_msg;
> +
> + /* Message printed when no objects were added or removed. */
> + const char *stopped_no_change_msg;
> +
> + /* Value of the MI "object-kind" field. */
> + const char *object_kind;
> +};
> +
> +/* Strings for shared libraries. */
> +
> +extern const print_solib_event_strings
> + print_solib_event_strings_shared_library;
> +
> +/* Strings for GPU code objects. */
> +
> +extern const print_solib_event_strings
> + print_solib_event_strings_gpu_code_object;
> +
> /* A helper function that prints a shared library stopped event.
> IS_CATCHPOINT is true if the event is due to a "catch load"
> catchpoint, false otherwise. */
>
> -extern void print_solib_event (bool is_catchpoint);
> +extern void print_solib_event (bool is_catchpoint,
> + const print_solib_event_strings &strings);
>
> /* Print a message describing any user-breakpoints set at PC. This
> concerns with logical breakpoints, so we match program spaces, not
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 4bd5357c587..397f0916a6d 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -22536,14 +22536,15 @@ The surrounding square brackets are optional.
> @item set stop-on-solib-events @r{[}on@r{|}off@r{]}
> @kindex set stop-on-solib-events
> This command controls whether @value{GDBN} should give you control
> -when the dynamic linker notifies it about some shared library event.
> -The most common event of interest is loading or unloading of a new
> -shared library.
> +when the dynamic linker notifies it about some shared library event,
> +or when GPU code objects are loaded or unloaded.
> +The most common events of interest are loading or unloading of a new
> +shared library or code object.
>
> @item show stop-on-solib-events
> @kindex show stop-on-solib-events
> Show whether @value{GDBN} stops and gives you control when shared
> -library events happen.
> +library events or GPU code object events happen.
> @end table
>
> Shared libraries are also supported in many cross or remote debugging
> @@ -32695,6 +32696,10 @@ The inferior has stopped due to a library being loaded or unloaded.
> This can happen when @code{stop-on-solib-events} (@pxref{Files}) is
> set or when a @code{catch load} or @code{catch unload} catchpoint is
> in use (@pxref{Set Catchpoints}).
> +
> +This stop reason may include an optional @code{object-kind} field
> +to distinguish different types of loadable objects. Currently defined
> +values are @code{gpu-code-object} for GPU code objects.
> @item fork
> The inferior has forked. This is reported when @code{catch fork}
> (@pxref{Set Catchpoints}) has been used.
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index 47966cf72db..3492821f4a9 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -413,8 +413,8 @@ get_detach_signal (process_stratum_target *proc_target, ptid_t ptid)
>
> static struct cmd_list_element *stop_command;
>
> -/* See infrun.h. */
> -
> +/* True if we want to give control to the user when we're notified
> + of shared library events by the dynamic linker. */
> bool stop_on_solib_events;
>
> /* Enable or disable optional shared library event breakpoints
> @@ -10850,9 +10850,10 @@ leave it stopped or free to run as needed."),
> &stop_on_solib_events, _("\
> Set stopping for shared library events."), _("\
> Show stopping for shared library events."), _("\
> -If on, gdb will give control to the user when the dynamic linker\n\
> -notifies gdb of shared library events. The most common event of interest\n\
> -to the user would be loading/unloading of a new library."),
> +If enabled, gdb will give control to the user when the dynamic linker\n\
> +notifies gdb of shared library events, or when GPU code objects are loaded\n\
> +or unloaded. The most common events of interest to the user would be\n\
> +loading/unloading of a new library or code object."),
> set_stop_on_solib_events,
> show_stop_on_solib_events,
> &setlist, &showlist);
> diff --git a/gdb/testsuite/gdb.rocm/solib-event-kernel.cpp b/gdb/testsuite/gdb.rocm/solib-event-kernel.cpp
> new file mode 100644
> index 00000000000..b2c47e26879
> --- /dev/null
> +++ b/gdb/testsuite/gdb.rocm/solib-event-kernel.cpp
> @@ -0,0 +1,27 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> + Copyright 2026 Free Software Foundation, Inc.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +/* Device kernel for solib-event test.
> + Compiled with --cuda-device-only to produce .co file. */
> +
> +#include <hip/hip_runtime.h>
> +
> +extern "C" __global__ void
> +test_kernel ()
> +{
> + asm volatile ("s_nop 1");
> +}
> diff --git a/gdb/testsuite/gdb.rocm/solib-event.cpp b/gdb/testsuite/gdb.rocm/solib-event.cpp
> new file mode 100644
> index 00000000000..823ab5629e1
> --- /dev/null
> +++ b/gdb/testsuite/gdb.rocm/solib-event.cpp
> @@ -0,0 +1,105 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> + Copyright 2026 Free Software Foundation, Inc.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include "rocm-test-utils.h"
> +#include <hip/hip_runtime.h>
> +#include <fstream>
> +#include <vector>
> +
> +/* Test file:// events (hipModuleLoad). */
> +static void
> +test_file_load (const char *module_path)
> +{
> + hipModule_t module;
> + CHECK (hipModuleLoad (&module, module_path));
> +
> + hipFunction_t function;
> + CHECK (hipModuleGetFunction (&function, module, "test_kernel"));
> +
> + CHECK (hipModuleLaunchKernel (function, 1, 1, 1, 1, 1, 1,
> + 0, nullptr, nullptr, nullptr));
> +
> + CHECK (hipDeviceSynchronize ());
> + CHECK (hipModuleUnload (module));
> +}
> +
> +/* Test memory:// events (hipModuleLoadData). */
> +static void
> +test_memory_load (const char *module_path)
> +{
> + /* Read module file into memory buffer. */
> + std::ifstream mod (module_path, std::ios::binary | std::ios::ate);
> + if (!mod.is_open ())
> + {
> + fprintf (stderr, "Failed to open module file\n");
> + exit (EXIT_FAILURE);
> + }
> +
> + size_t module_size = mod.tellg ();
> + mod.seekg (0, std::ios::beg);
> + std::vector<unsigned char> module_buffer (module_size);
> +
> + if (!mod.read (reinterpret_cast<char *> (module_buffer.data ()),
> + module_size))
> + {
> + fprintf (stderr, "Failed to read module into memory\n");
> + exit (EXIT_FAILURE);
> + }
> + mod.close ();
> +
> + /* Load from memory buffer. */
> + hipModule_t module;
> + CHECK (hipModuleLoadData (&module, module_buffer.data ()));
> +
> + hipFunction_t function;
> + CHECK (hipModuleGetFunction (&function, module, "test_kernel"));
> +
> + CHECK (hipModuleLaunchKernel (function, 1, 1, 1, 1, 1, 1,
> + 0, nullptr, nullptr, nullptr));
> +
> + CHECK (hipDeviceSynchronize ());
> + CHECK (hipModuleUnload (module));
> +}
> +
> +__global__ void
> +warm_up ()
> +{
> +}
> +
> +int
> +main (int argc, char **argv)
> +{
> + if (argc != 2)
> + {
> + fprintf (stderr, "Usage: %s <module_path>\n", argv[0]);
> + return EXIT_FAILURE;
> + }
> +
> + const char *module_path = argv[1];
> +
> + /* Submit an empty kernel to force the runtime to do any necessary
> + setup which might include loading internal code objects. */
> + warm_up<<<1, 1>>>();
> + CHECK (hipDeviceSynchronize ());
> +
> + /* Enable SOLIB events here. */
> +
> + test_file_load (module_path);
> + test_memory_load (module_path);
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.rocm/solib-event.exp b/gdb/testsuite/gdb.rocm/solib-event.exp
> new file mode 100644
> index 00000000000..87f12ccdb46
> --- /dev/null
> +++ b/gdb/testsuite/gdb.rocm/solib-event.exp
> @@ -0,0 +1,234 @@
> +# Copyright 2026 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +# Test that "set stop-on-solib-events on" works for GPU code objects.
> +# Tests both file:// events (hipModuleLoad) and memory:// events
> +# (hipModuleLoadData), as well as unload events (hipModuleUnload).
> +# Also tests that the default setting (off) does not stop execution.
> +# Also tests MI output includes object-kind field.
> +
> +load_lib rocm.exp
> +load_lib mi-support.exp
> +
> +require allow_hip_tests
> +
> +standard_testfile .cpp
> +
> +# Build GPU code object.
> +set kernel_srcfile ${testfile}-kernel.cpp
> +set hipmodule_path [standard_output_file ${testfile}.co]
> +
> +if {[gdb_compile $srcdir/$subdir/$kernel_srcfile \
> + $hipmodule_path object \
> + {debug hip additional_flags=--cuda-device-only}] != ""} {
> + return
> +}
> +
> +# Build host executable.
> +if {[build_executable "failed to prepare" $testfile $srcfile \
> + {debug hip}] == -1} {
> + return
> +}
> +
> +proc test_disabled {} {
> + with_rocm_gpu_lock {
> + clean_restart $::testfile
> +
> + if {![runto_main -inferior-args $::hipmodule_path]} {
> + return
> + }
> +
> + # Set breakpoint at program exit and verify execution continues
> + # through all code object load/unload events without stopping.
> + gdb_breakpoint [gdb_get_line_number "return 0;"]
> + gdb_test "continue" \
> + "Breakpoint .* main .*return 0.*" \
> + "no stops for code object events when disabled"
> + }
> +}
> +
> +proc test_enabled {} {
> + with_rocm_gpu_lock {
> + clean_restart $::testfile
> +
> + if {![runto_main -inferior-args $::hipmodule_path]} {
> + return
> + }
> +
> + gdb_breakpoint [gdb_get_line_number "Enable SOLIB events here"] -temporary
> + gdb_continue_to_breakpoint "at enable solib-event"
> +
> + gdb_test_no_output "set stop-on-solib-events on"
> +
> + with_test_prefix "file://" {
> + gdb_breakpoint "test_file_load" -temporary
> + gdb_continue_to_breakpoint "at test_file_load"
> +
> + # Test 1: file:// load event (hipModuleLoad).
> + gdb_test "continue" \
> + "Stopped due to GPU code object event.*Inferior loaded file://\[^\r\n\]+" \
> + "load"
> +
> + # Test 2: file:// unload event (hipModuleUnload).
> + gdb_test "continue" \
> + "Stopped due to GPU code object event.*Inferior unloaded file://\[^\r\n\]+" \
> + "unload"
> + }
> +
> + with_test_prefix "memory://" {
> + gdb_breakpoint "test_memory_load" -temporary
> + gdb_continue_to_breakpoint "at test_memory_load"
> +
> + # Test 3: memory:// load event (hipModuleLoadData).
> + gdb_test "continue" \
> + "Stopped due to GPU code object event.*Inferior loaded memory://\[^\r\n\]+" \
> + "load"
> +
> + # Test 4: memory:// unload event (hipModuleUnload).
> + gdb_test "continue" \
> + "Stopped due to GPU code object event.*Inferior unloaded memory://\[^\r\n\]+" \
> + "unload"
> + }
> + }
> +}
> +
> +proc test_mi_output {} {
> + global MIFLAGS
> + global hipmodule_path
> + global mi_gdb_prompt
> +
> + set MIFLAGS "-i=mi2"
> +
> + with_rocm_gpu_lock {
> + if {[mi_clean_restart $::testfile]} {
> + return
> + }
> +
> + # Set inferior arguments before running to main.
> + mi_gdb_test "-gdb-set args $hipmodule_path" "\\^done" \
> + "set inferior arguments"
> +
> + if {![mi_runto_main]} {
> + return
> + }
> +
> + mi_create_breakpoint [gdb_get_line_number "Enable SOLIB events here"] \
> + "insert breakpoint at enable marker"
> +
> + mi_send_resuming_command "exec-continue" "continue to enable marker"
> + # The warm_up<<<>>>() kernel loads code objects here, emitting
> + # async =library-loaded notifications even though stop-on-solib-events=off.
> + # Consume these async notifications before expecting the breakpoint stop.
> + gdb_expect {
> + -re "=library-loaded\[^\r\n\]*\r\n" {
> + exp_continue
> + }
> + -re "\\*stopped,reason=\"breakpoint-hit\"\[^\r\n\]*\r\n$mi_gdb_prompt" {
> + pass "stop at enable marker"
> + }
> + timeout {
> + fail "stop at enable marker (timeout)"
> + }
> + }
> +
> + mi_gdb_test "111-gdb-set stop-on-solib-events on" "111\\^done" \
> + "set stop-on-solib-events via MI"
> +
> + mi_create_breakpoint "test_file_load" "insert breakpoint at test_file_load"
> +
> + # Use manual gdb_expect instead of mi_expect_stop to avoid triggering
> + # duplicate MI notification bug in vanilla GDB.
> + mi_send_resuming_command "exec-continue" "continue to test_file_load"
> +
> + set breakpoint_modified_count 0
> + set console_output_count 0
> + set stopped_count 0
> +
> + gdb_expect {
> + -re "=breakpoint-modified,bkpt=\{number=\"\[0-9\]+\"" {
> + incr breakpoint_modified_count
> + exp_continue
> + }
> + -re "~\"\\\\n\"\r\n" {
> + incr console_output_count
> + exp_continue
> + }
> + -re "~\"Thread \[0-9\]+ .*hit Breakpoint \[0-9\]+, test_file_load" {
> + exp_continue
> + }
> + -re "~\[^\r\n\]*\r\n" {
> + exp_continue
> + }
> + -re "\\*stopped,reason=\"breakpoint-hit\".*func=\"test_file_load\"\[^\r\n\]*\r\n$mi_gdb_prompt" {
> + incr stopped_count
> + if {$breakpoint_modified_count == 1 && $stopped_count == 1} {
> + pass "stop at test_file_load"
> + } else {
> + fail "stop at test_file_load (breakpoint-modified=$breakpoint_modified_count, stopped=$stopped_count)"
> + }
> + }
> + timeout {
> + fail "stop at test_file_load (timeout)"
> + }
> + }
> +
> + # Check for GPU code object load event with object-kind field.
> + mi_send_resuming_command "exec-continue" "continue for GPU load event"
> +
> + set library_loaded_count 0
> + set solib_stopped_count 0
> + set has_object_kind 0
> +
> + gdb_expect {
> + -re "=library-loaded\[^\r\n\]*\r\n" {
> + incr library_loaded_count
> + exp_continue
> + }
> + -re "~\"Stopped due to GPU code object event" {
> + exp_continue
> + }
> + -re "~\[^\r\n\]*\r\n" {
> + exp_continue
> + }
> + -re "\\*stopped,reason=\"solib-event\".*object-kind=\"gpu-code-object\".*library=\[^\r\n\]*\r\n" {
> + incr solib_stopped_count
> + set has_object_kind 1
> + exp_continue
> + }
> + -re "$mi_gdb_prompt" {
> + if {$solib_stopped_count == 1 && $has_object_kind == 1} {
> + pass "verify object-kind field in MI output"
> + } else {
> + fail "verify object-kind field in MI output (stopped=$solib_stopped_count, has_object_kind=$has_object_kind)"
> + }
> + }
> + timeout {
> + fail "verify object-kind field in MI output (timeout)"
> + }
> + }
> + }
> +}
> +
> +with_test_prefix "stop-on-solib-events=off" {
> + test_disabled
> +}
> +
> +with_test_prefix "stop-on-solib-events=on" {
> + test_enabled
> +}
> +
> +with_test_prefix "MI output" {
> + test_mi_output
> +}
> diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
> index cd7e0a61189..20f2a9bbc96 100644
> --- a/gdb/testsuite/lib/mi-support.exp
> +++ b/gdb/testsuite/lib/mi-support.exp
> @@ -1250,7 +1250,7 @@ proc mi_expect_stop { reason func args file line extra test } {
> }
>
> if { $reason == "solib-event" } {
> - set pattern "\\*stopped,reason=\"solib-event\",thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re"
> + set pattern "\\*stopped,reason=\"solib-event\"(,object-kind=\"\[^\"]*\")?,thread-id=\"$decimal\",stopped-threads=$any\r\n($thread_selected_re|$breakpoint_re)*$prompt_re"
> verbose -log "mi_expect_stop: expecting: $pattern"
> gdb_expect {
> -re "$pattern" {
>
> base-commit: 60b08e6b3adf81fbd95cc69511cac640714174e2
> --
> 2.43.0
next prev parent reply other threads:[~2026-10-12 14:33 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-24 13:04 [PATCH] " Bratislav Filipovic
2026-09-28 20:03 ` Simon Marchi
2026-09-28 20:33 ` Simon Marchi
2026-10-01 10:35 ` Bratislav Filipovic
2026-10-01 10:54 ` Eli Zaretskii
2026-10-01 19:26 ` Simon Marchi
2026-10-08 13:09 ` [PATCH v3] " Bratislav Filipovic
2026-10-12 14:32 ` Lancelot SIX [this message]
2026-10-28 17:17 ` [PATCH v4] " Bratislav Filipovic
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2j2c6ytzi7ooevyncfwj6siff45zs3loivxc6siuy2tqnjxzrb@logfn6xqblyw \
--to=lancelot.six@amd.com \
--cc=bfilipov@amd.com \
--cc=gdb-patches@sourceware.org \
--cc=simon.marchi@polymtl.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox