-
Notifications
You must be signed in to change notification settings - Fork 27
gdb: Implement stop-on-solib-events for GPU code objects #235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: amd-staging
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,13 +38,15 @@ | |
| #include "gdbthread.h" | ||
| #include "inf-loop.h" | ||
| #include "inferior.h" | ||
| #include "infrun.h" | ||
| #include "location.h" | ||
| #include "objfiles.h" | ||
| #include "observable.h" | ||
| #include "registry.h" | ||
| #include "solib.h" | ||
| #include "target.h" | ||
| #include "tid-parse.h" | ||
| #include "ui-out.h" | ||
| #include "bfd/elf-bfd.h" | ||
| #include "elf/amdgpu.h" | ||
| #include "cp-support.h" | ||
|
|
@@ -284,6 +286,11 @@ struct amd_dbgapi_inferior_info | |
| /* List of pending events the amd-dbgapi target retrieved from the dbgapi. */ | ||
| std::list<std::pair<ptid_t, target_waitstatus>> wave_events; | ||
|
|
||
| /* Flag to track if a CODE_OBJECT_LIST_UPDATED event was seen during | ||
| process_event_queue. Used to implement stop-on-solib-events for GPU | ||
| code objects. */ | ||
| bool code_object_list_updated = false; | ||
|
|
||
| /* Map of threads with ongoing displaced steps to corresponding amd-dbgapi | ||
| displaced stepping handles. */ | ||
| gdb::unordered_map<thread_info *, | ||
|
|
@@ -832,6 +839,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 | ||
|
|
@@ -882,6 +890,12 @@ amd_dbgapi_target_breakpoint::check_status (struct bpstat *bs) | |
|
|
||
| require_forward_progress (info, false); | ||
|
|
||
| /* Clear the flag so event processing below can detect a new | ||
| CODE_OBJECT_LIST_UPDATED event. This flag is reset at the top of | ||
| check_status before each use to ensure we only see events from the | ||
| current stop. */ | ||
| info.code_object_list_updated = false; | ||
|
amd-bfilipov marked this conversation as resolved.
|
||
|
|
||
| /* 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 | ||
|
|
@@ -910,6 +924,74 @@ 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 GPU code object events. This | ||
| implements stop-on-solib-events for GPU code objects. */ | ||
| if (info.code_object_list_updated | ||
| && (stop_on_solib_events == STOP_SOLIB_GPU | ||
| || stop_on_solib_events == STOP_SOLIB_ALL)) | ||
| { | ||
| bs->stop = true; | ||
| bs->print = true; | ||
| /* Allow print_it () to print the GPU code object event message. */ | ||
| bs->print_it = print_it_normal; | ||
| } | ||
|
lumachad marked this conversation as resolved.
|
||
| } | ||
|
|
||
| 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 set to STOP_SOLIB_GPU or STOP_SOLIB_ALL. */ | ||
| bool any_deleted = !current_program_space->deleted_solibs.empty (); | ||
| bool any_added = !current_program_space->added_solibs.empty (); | ||
|
|
||
| if (current_uiout->is_mi_like_p ()) | ||
| { | ||
| current_uiout->field_string | ||
| ("reason", async_reason_lookup (EXEC_ASYNC_SOLIB_EVENT)); | ||
| current_uiout->field_string ("object-kind", "gpu-code-object"); | ||
| } | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both GPU code-object events and CPU shared-library events emit
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 |
||
| if (any_added || any_deleted) | ||
| current_uiout->text (_("Stopped due to GPU code object event:\n")); | ||
|
amd-bfilipov marked this conversation as resolved.
|
||
| else | ||
| current_uiout->text (_("Stopped due to GPU code object event (no " | ||
| "code objects added or removed)\n")); | ||
|
Comment on lines
+961
to
+962
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you know if/when this happens? Why would we have a code object event where code objects haven't been added or removed?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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. |
||
|
|
||
| if (any_deleted) | ||
| { | ||
| current_uiout->text (_(" Inferior unloaded ")); | ||
| ui_out_emit_list list_emitter (current_uiout, "removed"); | ||
| bool first = true; | ||
| for (const std::string &name : current_program_space->deleted_solibs) | ||
| { | ||
| if (!first) | ||
| current_uiout->text (" "); | ||
| first = false; | ||
| current_uiout->field_string ("code-object", name); | ||
| current_uiout->text ("\n"); | ||
| } | ||
| } | ||
|
|
||
| if (any_added) | ||
| { | ||
| current_uiout->text (_(" Inferior loaded ")); | ||
| ui_out_emit_list list_emitter (current_uiout, "added"); | ||
| bool first = true; | ||
| for (solib *iter : current_program_space->added_solibs) | ||
| { | ||
| if (!first) | ||
| current_uiout->text (" "); | ||
| first = false; | ||
| current_uiout->field_string ("code-object", iter->name); | ||
| current_uiout->text ("\n"); | ||
| } | ||
| } | ||
|
|
||
| return PRINT_NOTHING; | ||
| } | ||
|
|
||
| bool | ||
|
|
@@ -2120,6 +2202,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 ()); | ||
| info.code_object_list_updated = true; | ||
| handle_solib_event (); | ||
| break; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| /* 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 "rocm-test-utils.h" | ||
| #include <hip/hip_runtime.h> | ||
|
|
||
| extern "C" __global__ void | ||
| test_kernel () | ||
| { | ||
| NOP (1); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| /* 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<char> module_buffer (module_size); | ||
|
|
||
| if (!mod.read (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)); | ||
| } | ||
|
|
||
| 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]; | ||
|
|
||
| test_file_load (module_path); | ||
| test_memory_load (module_path); | ||
|
|
||
| return 0; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the comment does not mention that this flag is reset at the top of
check_statusbefore each use. A brief note would help future readers understand the intended lifetime.