Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions gdb/amd-dbgapi-target.c
Original file line number Diff line number Diff line change
Expand Up @@ -2076,14 +2076,18 @@ process_one_event (amd_dbgapi_inferior_info &info,
if (thread == nullptr)
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
breakpoint instruction.
/* If the wave is stopped because of a software breakpoint or an abort
trap (s_trap 2, used by __builtin_verbose_trap), the program counter
Comment thread
amd-bfilipov marked this conversation as resolved.
needs to be adjusted so that it points to the trap instruction.

For abort traps, this ensures the PC remains in the inlined frame
containing DWARF debug info for verbose trap messages.

When dealing with a corefile, it is expected that the PC has
been adjusted before generating the corefile, so no need to
re-do it now. */
if ((stop_reason & AMD_DBGAPI_WAVE_STOP_REASON_BREAKPOINT) != 0
if (((stop_reason & AMD_DBGAPI_WAVE_STOP_REASON_BREAKPOINT) != 0
|| (stop_reason & AMD_DBGAPI_WAVE_STOP_REASON_ASSERT_TRAP) != 0)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, I am not sure this should be the way to go.

I have ROCm/rocm-systems#9776 to change the trap handler behaviour so this is not necessary. Such change would fix other issues reported independent to this PR.

&& get_inferior_core_bfd (info.inf) == nullptr)
{
regcache *regcache = get_thread_regcache (thread);
Expand Down
31 changes: 31 additions & 0 deletions gdb/amd64-linux-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -2088,6 +2088,33 @@ amd64_linux_fetch_hiperr_parameters (frame_info_ptr frame)
return amd64_fetch_hiperr_parameters (frame, struct_addr);
}

/* Return true if the inline frame FUNC should be shown when stopped
Comment thread
amd-bfilipov marked this conversation as resolved.
due to STOP_SIGNAL. This allows showing verbose trap inline frames
on x86_64. */

static bool
amd64_linux_show_verbose_trap_inline_frame (struct gdbarch *gdbarch,
const struct symbol *func,
enum gdb_signal stop_signal)
{
/* Only show verbose trap frames when stopped due to illegal instruction.
The ud2 instruction used by verbose traps on x86_64 generates SIGILL.
This ensures we only show the frame when the trap actually fired,
not when user stepped into it with commands like "step". */
if (stop_signal != GDB_SIGNAL_ILL)
return false;

/* Check if this is a verbose trap inline frame by looking for the
compiler-generated function name pattern. */
const char *name = func->linkage_name ();
if (name == nullptr)
return false;

/* Verbose trap frames have names like:
"__clang_trap_msg$<category>$<message>" */
return startswith (name, "__clang_trap_msg$");
}

static void
amd64_linux_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch,
int num_disp_step_buffers)
Expand Down Expand Up @@ -2154,6 +2181,10 @@ amd64_linux_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch,
/* Extract hiperr parameters for 'catch hiperr'. */
set_gdbarch_fetch_hiperr_parameters
(gdbarch, amd64_linux_fetch_hiperr_parameters);

/* Show verbose trap inline frames when stopped due to abort. */
set_gdbarch_show_verbose_trap_inline_frame
(gdbarch, amd64_linux_show_verbose_trap_inline_frame);
}

static void
Expand Down
27 changes: 27 additions & 0 deletions gdb/amdgpu-tdep.c
Original file line number Diff line number Diff line change
Expand Up @@ -2019,6 +2019,30 @@ amdgpu_supports_arch_info (const struct bfd_arch_info *info)
return status == AMD_DBGAPI_STATUS_SUCCESS;
}

/* Implementation of gdbarch_show_verbose_trap_inline_frame for AMDGPU. */

static bool
amdgpu_show_verbose_trap_inline_frame (struct gdbarch *gdbarch,
const struct symbol *func,
enum gdb_signal stop_signal)
{
/* Only show verbose trap frames when stopped due to abort signal.
This ensures we only show the frame when the trap actually fired,
not when user stepped into it with commands like "step". */
if (stop_signal != GDB_SIGNAL_ABRT)
return false;

/* Check if this is a verbose trap inline frame by looking for the
compiler-generated function name pattern. */
const char *name = func->linkage_name ();
if (name == nullptr)
return false;

/* Verbose trap frames have names like:
"__clang_trap_msg$<category>$<message>" */
return startswith (name, "__clang_trap_msg$");
}

static struct gdbarch *
amdgpu_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
Expand Down Expand Up @@ -2271,6 +2295,9 @@ amdgpu_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)

set_gdbarch_decr_pc_after_break (gdbarch, pc_adjust);

set_gdbarch_show_verbose_trap_inline_frame
Comment thread
amd-bfilipov marked this conversation as resolved.
(gdbarch, amdgpu_show_verbose_trap_inline_frame);

/* Get info about address spaces. */
size_t address_space_count;
amd_dbgapi_address_space_id_t *address_spaces;
Expand Down
11 changes: 11 additions & 0 deletions gdb/arch-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1613,6 +1613,17 @@ core_file_exec_context::environment () const
return e;
}

/* See arch-utils.h. */

bool
default_show_verbose_trap_inline_frame (struct gdbarch *gdbarch,
const struct symbol *func,
enum gdb_signal stop_signal)
{
/* By default, do not show verbose trap inline frames. */
return false;
}

INIT_GDB_FILE (gdbarch_utils)
{
add_setshow_enum_cmd ("endian", class_support,
Expand Down
5 changes: 5 additions & 0 deletions gdb/arch-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,4 +451,9 @@ extern int default_supported_lanes_count (struct gdbarch *gdbarch,
extern std::vector<addr_range> default_get_watchable_aliases
(struct gdbarch *gdbarch, ptid_t ptid, int simd_lane, addr_range range);

/* Default implementation of gdbarch_show_verbose_trap_inline_frame. */
extern bool default_show_verbose_trap_inline_frame
(struct gdbarch *gdbarch, const struct symbol *func,
enum gdb_signal stop_signal);

#endif /* GDB_ARCH_UTILS_H */
22 changes: 22 additions & 0 deletions gdb/gdbarch-gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ struct gdbarch
gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer;
gdbarch_show_verbose_trap_inline_frame_ftype *show_verbose_trap_inline_frame = default_show_verbose_trap_inline_frame;
};

/* Create a new ``struct gdbarch'' based on information provided by
Expand Down Expand Up @@ -547,6 +548,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of core_parse_exec_context, invalid_p == 0. */
/* Skip verify of shadow_stack_push, has predicate. */
/* Skip verify of get_shadow_stack_pointer, invalid_p == 0. */
/* Skip verify of show_verbose_trap_inline_frame, invalid_p == 0. */
if (!log.empty ())
internal_error (_("verify_gdbarch: the following are invalid ...%s"),
log.c_str ());
Expand Down Expand Up @@ -1421,6 +1423,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
gdb_printf (file,
"gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
host_address_to_string (gdbarch->get_shadow_stack_pointer));
gdb_printf (file,
"gdbarch_dump: show_verbose_trap_inline_frame = <%s>\n",
host_address_to_string (gdbarch->show_verbose_trap_inline_frame));
if (gdbarch->dump_tdep != nullptr)
gdbarch->dump_tdep (gdbarch, file);
}
Expand Down Expand Up @@ -5600,3 +5605,20 @@ set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
{
gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer;
}

bool
gdbarch_show_verbose_trap_inline_frame (struct gdbarch *gdbarch, const struct symbol *func, enum gdb_signal stop_signal)
{
gdb_assert (gdbarch != nullptr);
gdb_assert (gdbarch->show_verbose_trap_inline_frame != nullptr);
if (gdbarch_debug >= 2)
gdb_printf (gdb_stdlog, "gdbarch_show_verbose_trap_inline_frame called\n");
return gdbarch->show_verbose_trap_inline_frame (gdbarch, func, stop_signal);
}

void
set_gdbarch_show_verbose_trap_inline_frame (struct gdbarch *gdbarch,
gdbarch_show_verbose_trap_inline_frame_ftype show_verbose_trap_inline_frame)
{
gdbarch->show_verbose_trap_inline_frame = show_verbose_trap_inline_frame;
}
18 changes: 18 additions & 0 deletions gdb/gdbarch-gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1852,3 +1852,21 @@ void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stac
using gdbarch_get_shadow_stack_pointer_ftype = std::optional<CORE_ADDR> (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer);

/* Return true if the inline frame represented by FUNC should NOT be skipped
when stopped due to STOP_SIGNAL. This allows architectures to show
compiler-generated inline frames that contain verbose trap messages
(e.g., __builtin_verbose_trap).

By default, inline frames are skipped to provide better stepping experience.
However, for verbose trap scenarios, the inline frame contains important
diagnostic information that should be visible in backtraces.

Architectures can use this hook to detect verbose trap frames by checking
both the symbol name (e.g., starts with "__clang_trap_msg$") and the stop
signal (e.g., GDB_SIGNAL_ABRT for abort traps), returning true only when
both conditions indicate a verbose trap scenario. */

using gdbarch_show_verbose_trap_inline_frame_ftype = bool (struct gdbarch *gdbarch, const struct symbol *func, enum gdb_signal stop_signal);
bool gdbarch_show_verbose_trap_inline_frame (struct gdbarch *gdbarch, const struct symbol *func, enum gdb_signal stop_signal);
void set_gdbarch_show_verbose_trap_inline_frame (struct gdbarch *gdbarch, gdbarch_show_verbose_trap_inline_frame_ftype *show_verbose_trap_inline_frame);
23 changes: 23 additions & 0 deletions gdb/gdbarch_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -2944,3 +2944,26 @@
predefault="default_get_shadow_stack_pointer",
invalid=False,
)

Method(
comment="""
Return true if the inline frame represented by FUNC should NOT be skipped
when stopped due to STOP_SIGNAL. This allows architectures to show
compiler-generated inline frames that contain verbose trap messages
(e.g., __builtin_verbose_trap).

By default, inline frames are skipped to provide better stepping experience.
However, for verbose trap scenarios, the inline frame contains important
diagnostic information that should be visible in backtraces.

Architectures can use this hook to detect verbose trap frames by checking
both the symbol name (e.g., starts with "__clang_trap_msg$") and the stop
signal (e.g., GDB_SIGNAL_ABRT for abort traps), returning true only when
both conditions indicate a verbose trap scenario.
""",
type="bool",
name="show_verbose_trap_inline_frame",
params=[("const struct symbol *", "func"), ("enum gdb_signal", "stop_signal")],
predefault="default_show_verbose_trap_inline_frame",
invalid=False,
)
7 changes: 6 additions & 1 deletion gdb/inline-frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "regcache.h"
#include "symtab.h"
#include "frame.h"
#include "gdbarch.h"
#include "cli/cli-cmds.h"
#include "cli/cli-style.h"
#include <algorithm>
Expand Down Expand Up @@ -428,10 +429,14 @@ skip_inline_frames (thread_info *thread, bpstat *stop_chain)
which contains all of the inlined functions, we never skip this. */
int skipped_frames = 0;

struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
enum gdb_signal stop_signal = thread->stop_signal ();

for (const auto sym : function_symbols)
{
if (stopped_by_user_bp_inline_frame (sym, stop_chain)
|| sym == function_symbols.back ())
|| sym == function_symbols.back ()
|| gdbarch_show_verbose_trap_inline_frame (gdbarch, sym, stop_signal))
break;
Comment thread
amd-bfilipov marked this conversation as resolved.

++skipped_frames;
Expand Down
38 changes: 38 additions & 0 deletions gdb/testsuite/gdb.base/builtin_verbose_trap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* Copyright (C) 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/>.
*/

/* Regular inline function - inline frame should be skipped during stepping. */
__attribute__((always_inline)) inline int
add_numbers (int a, int b)
{
return a + b;
}

void
test_trap_function ()
{
int x = 1; /* Breakpoint here. */
int y = 2;
int z = add_numbers (x, y); /* Step over inline function. */
__builtin_verbose_trap ("check verbose", "This is verbose trap!");
}

int
main ()
{
test_trap_function ();
return 0;
}
60 changes: 60 additions & 0 deletions gdb/testsuite/gdb.base/builtin_verbose_trap.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (C) 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 __builtin_verbose_trap inline frames are shown when trap fires.
# __builtin_verbose_trap is a Clang-only builtin, added in Clang 17.

require {expr {[test_compiler_info clang*] && ![test_compiler_info {clang-1[0-6]-*}]}}

standard_testfile .cpp

if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} {
return
}

if {![runto_main]} {
return
}

# Set breakpoint in function before inline function call.
gdb_breakpoint "test_trap_function"

# Continue to breakpoint.
gdb_test "continue" \
".*Breakpoint.*test_trap_function.*" \
"hit breakpoint in function"

# Step through inline function calls with next.
# The inline frames for add_numbers should be hidden.
gdb_test "next" ".*" "next 1"
gdb_test "next" ".*" "next 2"

# Verify backtrace does NOT show add_numbers inline frame.
gdb_test "bt" \
".*test_trap_function.*" \
"inline frame not shown during stepping"

gdb_test_no_output "set confirm off"

# Continue to trigger the trap.
# CPU uses ud2 instruction which generates SIGILL (not SIGABRT like GPU).
gdb_test "continue" \
".*received signal SIGILL.*" \
"received trap signal"

# Verify verbose trap message appears in backtrace.
gdb_test "bt" \
".*This is verbose trap.*" \
"verbose trap message appears in backtrace"
Loading
Loading