gdb: Implement stop-on-solib-events for GPU code objects - #235
gdb: Implement stop-on-solib-events for GPU code objects#235amd-bfilipov wants to merge 3 commits into
Conversation
f665745 to
335c12d
Compare
|
Does this need a review? If so, we need to flip it to review. |
Wanted to check some things first, it's ready for review now. |
czidev-amd
left a comment
There was a problem hiding this comment.
IMHO, you need to check bs->print_it assign. Any other comment is non blocking.
9dd1ab3 to
79095ea
Compare
|
@amd-bfilipov When force-pushing, please make a comment clarifying what it is that you changed. Or go through the review comments and clarify for each of those what you've done. |
@lumachad Understood. Replied to comments. |
| set seen_code_object_loaded 0 | ||
|
|
||
| # Continue until we see GPU code object events or the program ends. | ||
| for {set i 0} {$i < 20} {incr i} { |
There was a problem hiding this comment.
I don't think we need this loop. See the comment above.
45ca7a7 to
ec49403
Compare
lumachad
left a comment
There was a problem hiding this comment.
This seems reasonable. But I wonder if whoever is chasing GPU bugs and wants to see only GPU solib/code object events would be best server by a switch that only stops for GPU code object events rather than all the non-interesting CPU solib events.
| current_uiout->text (_("Stopped due to GPU code object event (no " | ||
| "code objects added or removed)\n")); |
There was a problem hiding this comment.
Do you know if/when this happens? Why would we have a code object event where code objects haven't been added or removed?
There was a problem hiding this comment.
I don't know if it happens for GPU events but while testing I've seen "no code objects added or removed" events on CPU.
| #define CHECK(cmd) \ | ||
| { \ | ||
| hipError_t error = cmd; \ | ||
| if (error != hipSuccess) \ | ||
| { \ | ||
| fprintf (stderr, "error: '%s'(%d) at %s:%d\n", \ | ||
| hipGetErrorString (error), error, __FILE__, __LINE__); \ | ||
| exit (EXIT_FAILURE); \ | ||
| } \ | ||
| } | ||
|
|
||
| /* Prevent inlining to ensure kernel stays visible for debugging. */ | ||
| #define NOP(x) asm ("s_nop " #x) |
There was a problem hiding this comment.
Let's please include rocm-test-utils.h. We then don't need to define CHECK and NOP.
There was a problem hiding this comment.
Replaced custom CHECK and NOP macros with #include "rocm-test-utils.h" which provides the same definitions
| return | ||
| } | ||
|
|
||
| proc do_test {} { |
There was a problem hiding this comment.
IMHO we don't need this proc, but this style is used in some other tests, so I wouldn't require that you remove it.
There was a problem hiding this comment.
kept it as it is used in other tests
|
|
||
| for {set i 0} {$i < $max_continues} {incr i} { | ||
| gdb_test_multiple "continue" "continue looking for GPU event" { | ||
| -re "Stopped due to GPU code object event.*Inferior loaded (file://\[^\r\n\]+).*$::gdb_prompt $" { |
There was a problem hiding this comment.
Please use -wrap and remove the prompt anchor at the end. Also applies to the cases below.
There was a problem hiding this comment.
Added -wrap to all gdb_test_multiple patterns and removed explicit $::gdb_prompt $ anchors
| # CPU solib event, continue looping. | ||
| } | ||
| -re "Inferior.*exited.*$::gdb_prompt $" { | ||
| fail "program exited before GPU event" |
There was a problem hiding this comment.
We don't need this fail. There is no "pass" correspondence. The asserts below will emit FAILs anyway.
|
|
||
| if {$seen_gpu_solib_event} { | ||
| break | ||
| } |
There was a problem hiding this comment.
This seems unneeded. We already have a break at the single place where we set the boolean to true.
| # Verify we saw the GPU code object event. | ||
| gdb_assert {$seen_gpu_solib_event} "saw GPU code object event" | ||
|
|
||
| # Verify the loaded code object has the file:// prefix. | ||
| gdb_assert {$seen_file_prefix} "code object has file:// prefix" |
There was a problem hiding this comment.
These two flags are either both false or both true. There is only one case that sets both. We should either separate the scenarios (is there a scenario that emits a GPU event but no file prefix?) or only one flag would suffice.
There was a problem hiding this comment.
Merged seen_gpu_solib_event and seen_file_prefix into single flag
I'd think that the user gets frustrated by the CPU solib events in the first try, and then they quickly learn that they should rather turn the setting on at a later point after CPU libs are loaded. I wouldn't mind a separate "stop-on-gpu-solib-events" or a similar setting. It sounds useful. |
| process_event_queue. Used to implement stop-on-solib-events for GPU | ||
| code objects. */ | ||
| bool code_object_list_updated = false; | ||
|
|
There was a problem hiding this comment.
Nit: the comment does not mention that this flag is reset at the top of check_status before each use. A brief note would help future readers understand the intended lifetime.
| amd_dbgapi_target_breakpoint::print_it (const bpstat *bs) const | ||
| { | ||
| /* Check if this is a GPU code object event by looking at the solib lists. | ||
| We only reach here if check_status () set bs->print_it to print_it_normal, |
There was a problem hiding this comment.
The comment contradicts itself: the first sentence says "Check if this is a GPU code object event" but the rest establishes we already know it is. Suggested rewording:
/* 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. */| if (current_uiout->is_mi_like_p ()) | ||
| current_uiout->field_string ("reason", | ||
| async_reason_lookup (EXEC_ASYNC_SOLIB_EVENT)); | ||
|
|
There was a problem hiding this comment.
Both GPU code-object events and CPU shared-library events emit reason="solib-event" over MI. An MI front-end has no way to distinguish them without parsing free-form text. Consider a new async reason or an extra field such as "object-kind", "gpu-code-object" to allow programmatic differentiation.
There was a problem hiding this comment.
Added object-kind='gpu-code-object' field to MI output at amd-dbgapi-target.c:953, allowing MI frontends to distinguish GPU from CPU solib events programmatically
| current_uiout->text (_(" Inferior unloaded ")); | ||
| ui_out_emit_list list_emitter (current_uiout, "removed"); | ||
| for (int ix = 0; | ||
| ix < current_program_space->deleted_solibs.size (); |
There was a problem hiding this comment.
The deleted_solibs block uses an index-based for loop while the added_solibs block below uses a range-based loop. Please pick one style and apply it to both.
There was a problem hiding this comment.
Converted deleted_solibs to range-based loop
| #include <hip/hip_runtime.h> | ||
| #include <cstdlib> | ||
| #include <stdio.h> | ||
|
|
There was a problem hiding this comment.
Nit: mixed C and C++ headers — <cstdlib> (C++) alongside <stdio.h> (C). Please use <cstdio> instead of <stdio.h> for consistency, or drop <cstdlib> if EXIT_FAILURE is already provided by the HIP header.
There was a problem hiding this comment.
Removed all manual headers since rocm-test-utils.h includes both stdio.h and stdlib.h
| set seen_gpu_solib_event 0 | ||
| set seen_file_prefix 0 | ||
| set max_continues 20 | ||
|
|
There was a problem hiding this comment.
The magic number 20 has no explanation. Please add a comment saying why 20 iterations is a sufficient upper bound to cover all CPU solib events before the first GPU event.
There was a problem hiding this comment.
Added comment explaining that the limit of 20 iterations should be sufficient to cover all CPU shared library events that load before the first GPU code object
|
|
||
| for {set i 0} {$i < $max_continues} {incr i} { | ||
| gdb_test_multiple "continue" "continue looking for GPU event" { | ||
| -re "Stopped due to GPU code object event.*Inferior loaded (file://\[^\r\n\]+).*$::gdb_prompt $" { |
There was a problem hiding this comment.
gdb_test_multiple is missing a catch-all prompt arm. If GDB stops for an unexpected reason the call returns without setting any flag and the loop silently continues. Consider adding:
-re "$::gdb_prompt $" {
fail "unexpected stop before GPU code object event"
break
}There was a problem hiding this comment.
Added catch-all pattern with -wrap and fixed CPU solib pattern to include .* at end to match optional text like '(no libraries added or removed)'
| for {set i 0} {$i < $max_continues} {incr i} { | ||
| gdb_test_multiple "continue" "continue looking for GPU event" { | ||
| -re "Stopped due to GPU code object event.*Inferior loaded (file://\[^\r\n\]+).*$::gdb_prompt $" { | ||
| set seen_gpu_solib_event 1 |
There was a problem hiding this comment.
The test only verifies file:// code object events. The PR description shows both file:// and memory:// events occur. Consider also checking that memory:// events are printed correctly.
There was a problem hiding this comment.
Completely rewrote test using hipModule* API to test both file:// (hipModuleLoad) and memory:// (hipModuleLoadData) events, as well as unload events for both
|
This is a user-visible feature. I think it's worth adding a CHANGELOG entry so that it is mentioned in the release notes. |
| @@ -0,0 +1,48 @@ | |||
| /* This testcase is part of GDB, the GNU debugger. | |||
|
|
|||
| Copyright 2022-2026 Free Software Foundation, Inc. | |||
There was a problem hiding this comment.
why do we start from 2022?
| gdb_test_no_output "set stop-on-solib-events 1" | ||
|
|
||
| # Continue through solib events until we hit the GPU code object event. | ||
| # There may be CPU shared library events before the GPU event. |
There was a problem hiding this comment.
I am not sure the approach is the best one. For example, if someone runs in an environment where the environment variable HIP_ENABLE_DEFERRED_LOADING=0 all GPU code objects will be loaded before you reach main.
A better way would be to use the hipModule* series of functions to explicitly load a GPU code object at runtime. The testcase could run until the load, step over (or continue over) the load and check that we receive the GPU code object loading. This way, we control everything, which should ensure we have just one event coming.
You can check testcases such as gdb.rocm/code-object-load-while-breakpoint-hip or gdb.rocm/snapshot-objfile-on-load for examples.
There was a problem hiding this comment.
A better way would be to use the
hipModule*series of functions to explicitly load a GPU code object at runtime. The testcase could run until the load, step over (or continue over) the load and check that we receive the GPU code object loading. This way, we control everything, which should ensure we have just one event coming.
This sounds very good to me.
There was a problem hiding this comment.
Completely rewrote test using hipModule* API to test both file:// (hipModuleLoad) and memory:// (hipModuleLoadData) events, as well as unload events for both
GDB's "set stop-on-solib-events 1" setting was not working for GPU code objects loaded by the AMD ROCm runtime. While CPU shared library events correctly triggered stops when this setting was enabled, GPU code object load events were silently ignored. The root cause was in amd_dbgapi_target_breakpoint::check_status(), which unconditionally set bs->stop = 0 and bs->print_it = print_it_noop, 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 by checking the stop_on_solib_events global variable. The fix adds: 1. A code_object_list_updated flag to amd_dbgapi_inferior_info to track when AMD_DBGAPI_EVENT_KIND_CODE_OBJECT_LIST_UPDATED events are seen during process_event_queue(). 2. Logic in check_status() to check this flag after processing events, and update bs->stop, bs->print, and bs->print_it based on stop_on_solib_events. 3. A print_it() override to display "Stopped due to GPU code object event" to distinguish GPU events from CPU shared library events. This makes GPU code object load events behave consistently with CPU shared library events, allowing users to stop execution when GPU code objects are loaded for inspection and breakpoint placement. A test is included in gdb.rocm/solib-event.exp.
ec49403 to
45d0164
Compare
Modified stop-on-solib-events to accept integer values that control stopping on CPU shared libraries vs GPU code objects independently: 0 = Do not stop on shared library events 1 = Stop on CPU shared library events only 2 = Stop on GPU code object events only 3 = Stop on both CPU and GPU library events This follows GDB's precedent for integer-encoded mode settings (like annotation_level) and preserves backward compatibility - users who previously set stop-on-solib-events to 1 continue to get CPU-only behavior. The show command now displays a human-readable explanation of the current value alongside the numeric setting. Renamed solib-event test to gpu-solib-event to better reflect that it specifically tests GPU code object events. Updated the test to use value 2 (GPU events only), which eliminates the need to skip CPU shared library events during test execution. CPU solib event testing is already covered by existing GDB testsuite tests. Ticket: AIROCGDB-589
|
Added independent CPU/GPU control (Commit 2) Modified stop-on-solib-events to accept integer values for independent control:
This follows GDB's precedent for integer-encoded settings (like annotation_level). The show command now displays (gdb) show stop-on-solib-events Updated the test to use value 2, eliminating the need to filter out CPU shared library events during execution. |
Enhanced stop-on-solib-events to accept both numeric values (0-3) and
string keywords ("none", "cpu", "gpu", "all") for improved usability.
Implementation uses GDB's extra_literals mechanism, which provides:
- Automatic tab completion for string keywords
- Display translation (shows "cpu" instead of "1")
- Backward compatibility with numeric input
Users can now set the value using either format:
set stop-on-solib-events 2 # Numeric (existing)
set stop-on-solib-events gpu # String keyword (new)
Both formats are equivalent and produce the same internal value. The
show command displays the literal string when available, making the
current setting more readable.
This follows GDB's precedent used by settings like "unlimited" which
accept both numeric and string input.
Ticket: AIROCGDB-589
stop-on-solib-events support for GPU code objects
GDB's
set stop-on-solib-events 1setting 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 was not working for GPU code objects loaded by the AMD ROCm runtime.This PR implements
stop-on-solib-eventssupport for GPU code objects, making GPU code object load/unload events behave consistently with CPU shared library events.Related: AIROCGDB-589
Root Cause
The
amd_dbgapi_target_breakpoint::check_status()function unconditionally setbs->stop = 0andbs->print_it = print_it_noop, regardless of thestop_on_solib_eventssetting. This is in contrast tointernal_breakpoint::check_status()for CPU shared libraries, which respects the setting.Changes
Commit 1: Implement stop-on-solib-events for GPU code objects
Implementation:
code_object_list_updatedflag toamd_dbgapi_inferior_infoto track whenAMD_DBGAPI_EVENT_KIND_CODE_OBJECT_LIST_UPDATEDevents occurcheck_status()to check this flag after processing events and updatebs->stop,bs->print, andbs->print_itbased onstop_on_solib_eventsprocess_one_event()whenCODE_OBJECT_LIST_UPDATEDevents are seenCustom message printing:
print_it()method inamd_dbgapi_target_breakpointto print a custom message distinguishing GPU code object events from CPU shared library eventsfile://ormemory://URIsobject-kindfield set to"gpu-code-object"to allow IDE frontends to distinguish GPU from CPU solib eventsTesting:
gdb.rocm/gpu-solib-event.exptest to verify GPU code object events trigger stopshipModuleLoad()forfile://events andhipModuleLoadData()formemory://eventsCommit 2: Add GPU/CPU control to stop-on-solib-events setting
Implementation:
Modified
stop-on-solib-eventsto accept integer values that control stopping on CPU shared libraries vs GPU code objects independently:This follows GDB's precedent for integer-encoded mode settings (like
annotation_level) and preserves backward compatibility - users who previously setstop-on-solib-eventsto 1 continue to get CPU-only behavior.User experience improvements:
showcommand now displays a human-readable explanation of the current value:Test changes:
gpu-solib-event.exptest to use value 2 (GPU events only)Example Output
With the fix applied:
Note: With value 2, CPU shared library events are skipped entirely, showing only GPU code object events.
Backward Compatibility
stop-on-solib-eventsto 1 (or any non-zero value in the old behavior) continues to work for CPU librariesBenefits
object-kindfield allows programmatic distinction