From: Tom Tromey <tom@tromey.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tom@tromey.com>
Subject: [RFC] Show the selected frame in "bt"
Date: Fri, 29 May 2026 12:45:03 -0600 [thread overview]
Message-ID: <20260529184503.3263361-1-tom@tromey.com> (raw)
I've occasionally wished that "bt" would indicate the selected frame.
This patch implements this idea. In particular it marks the selected
frame with "*", similar to other "selected" output in gdb. (See that
other series where emoji were allowed in the "current" column of a
table; if that is ever resurrected, I'd expect the same treatment to
be applied here.)
Now the output looks like:
(gdb) bt
#0 0x00007ffff6e381fd in poll () from /lib64/libc.so.6
#1 0x000000000100b1ee in gdb_wait_for_event (block=1) at ../../binutils-gdb/gdbsupport/event-loop.cc:587
#2 0x000000000100a77f in gdb_do_one_event (mstimeout=-1) at ../../binutils-gdb/gdbsupport/event-loop.cc:263
* #3 0x00000000007d2c6a in interp::do_one_event (this=<optimized out>, mstimeout=-1) at ../../binutils-gdb/gdb/interps.h:90
#4 start_event_loop () at ../../binutils-gdb/gdb/main.c:400
#5 captured_command_loop () at ../../binutils-gdb/gdb/main.c:465
#6 0x00000000007d5715 in captured_main (context=context@entry=0x7fffffffdd10) at ../../binutils-gdb/gdb/main.c:1373
#7 gdb_main (args=args@entry=0x7fffffffdd50) at ../../binutils-gdb/gdb/main.c:1392
#8 0x0000000000452cd5 in main (argc=1, argv=0x7fffffffdea8) at ../../binutils-gdb/gdb/gdb.c:38
I think the main downside of this patch is that it uses a little more
horizontal space for the indicator.
This patch will not regression-test cleanly. There are many tests
that need an update to match the new output. However, I wanted to
send this out for conceptual approval before doing that work.
Let me know what you think.
---
gdb/extension-priv.h | 3 ++-
gdb/extension.c | 8 +++++---
gdb/extension.h | 3 ++-
gdb/frame.h | 3 ++-
gdb/mi/mi-cmd-stack.c | 8 ++++----
gdb/python/py-framefilter.c | 18 +++++++++++++----
gdb/python/python-internal.h | 3 ++-
gdb/stack.c | 39 +++++++++++++++++++++++++++---------
8 files changed, 61 insertions(+), 24 deletions(-)
diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
index 4ef87415413..9b0c4815399 100644
--- a/gdb/extension-priv.h
+++ b/gdb/extension-priv.h
@@ -186,7 +186,8 @@ struct extension_language_ops
(const struct extension_language_defn *,
const frame_info_ptr &frame, frame_filter_flags flags,
enum ext_lang_frame_args args_type,
- struct ui_out *out, int frame_low, int frame_high);
+ struct ui_out *out, int frame_low, int frame_high,
+ const frame_info_ptr &selected_frame);
/* Used for registering the ptwrite filter to the current thread. */
void (*apply_ptwrite_filter)
diff --git a/gdb/extension.c b/gdb/extension.c
index d8ef8123ab5..a57ad4a517a 100644
--- a/gdb/extension.c
+++ b/gdb/extension.c
@@ -521,7 +521,8 @@ apply_ext_lang_frame_filter (const frame_info_ptr &frame,
frame_filter_flags flags,
enum ext_lang_frame_args args_type,
struct ui_out *out,
- int frame_low, int frame_high)
+ int frame_low, int frame_high,
+ const frame_info_ptr &selected_frame)
{
for (const struct extension_language_defn *extlang : extension_languages)
{
@@ -531,8 +532,9 @@ apply_ext_lang_frame_filter (const frame_info_ptr &frame,
|| extlang->ops->apply_frame_filter == NULL)
continue;
status = extlang->ops->apply_frame_filter (extlang, frame, flags,
- args_type, out,
- frame_low, frame_high);
+ args_type, out,
+ frame_low, frame_high,
+ selected_frame);
/* We use the filters from the first extension language that has
applicable filters. Also, an error is reported immediately
rather than continue trying. */
diff --git a/gdb/extension.h b/gdb/extension.h
index e351b5b672e..af535199adb 100644
--- a/gdb/extension.h
+++ b/gdb/extension.h
@@ -302,7 +302,8 @@ extern int apply_ext_lang_val_pretty_printer
extern enum ext_lang_bt_status apply_ext_lang_frame_filter
(const frame_info_ptr &frame, frame_filter_flags flags,
enum ext_lang_frame_args args_type,
- struct ui_out *out, int frame_low, int frame_high);
+ struct ui_out *out, int frame_low, int frame_high,
+ const frame_info_ptr &selected_frame);
extern void apply_ext_lang_ptwrite_filter
(struct btrace_thread_info *btinfo);
diff --git a/gdb/frame.h b/gdb/frame.h
index 00485e6d1e2..ed344991330 100644
--- a/gdb/frame.h
+++ b/gdb/frame.h
@@ -899,7 +899,8 @@ extern void print_stack_frame (const frame_info_ptr &, int print_level,
extern void print_frame_info (const frame_print_options &fp_opts,
const frame_info_ptr &, int print_level,
enum print_what print_what, int args,
- int set_current_sal);
+ int set_current_sal,
+ const frame_info_ptr &selected_frame);
extern frame_info_ptr block_innermost_frame (const struct block *);
diff --git a/gdb/mi/mi-cmd-stack.c b/gdb/mi/mi-cmd-stack.c
index 3281be2b4dc..ba75b9d92d6 100644
--- a/gdb/mi/mi-cmd-stack.c
+++ b/gdb/mi/mi-cmd-stack.c
@@ -67,7 +67,7 @@ mi_apply_ext_lang_frame_filter (const frame_info_ptr &frame,
return apply_ext_lang_frame_filter (frame, flags,
(enum ext_lang_frame_args) print_values,
out,
- frame_low, frame_high);
+ frame_low, frame_high, {});
}
/* Print a list of the stack frames. Args can be none, in which case
@@ -158,7 +158,7 @@ mi_cmd_stack_list_frames (const char *command, const char *const *argv,
result = apply_ext_lang_frame_filter (get_current_frame (), flags,
NO_VALUES, current_uiout,
- py_frame_low, frame_high);
+ py_frame_low, frame_high, {});
}
/* Run the inbuilt backtrace if there are no filters registered, or
@@ -175,7 +175,7 @@ mi_cmd_stack_list_frames (const char *command, const char *const *argv,
/* Print the location and the address always, even for level 0.
If args is 0, don't print the arguments. */
print_frame_info (user_frame_print_options,
- fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0);
+ fi, 1, LOC_AND_ADDRESS, 0 /* args */, 0, {});
}
}
}
@@ -762,5 +762,5 @@ mi_cmd_stack_info_frame (const char *command, const char *const *argv,
error (_("-stack-info-frame: No arguments allowed"));
print_frame_info (user_frame_print_options,
- get_selected_frame (NULL), 1, LOC_AND_ADDRESS, 0, 1);
+ get_selected_frame (NULL), 1, LOC_AND_ADDRESS, 0, 1, {});
}
diff --git a/gdb/python/py-framefilter.c b/gdb/python/py-framefilter.c
index f256a26807c..11463746787 100644
--- a/gdb/python/py-framefilter.c
+++ b/gdb/python/py-framefilter.c
@@ -761,7 +761,8 @@ static enum ext_lang_bt_status
py_print_frame (PyObject *filter, frame_filter_flags flags,
enum ext_lang_frame_args args_type,
struct ui_out *out, int indent,
- levels_printed_hash &levels_printed)
+ levels_printed_hash &levels_printed,
+ const frame_info_ptr &selected_frame)
{
int has_addr = 0;
CORE_ADDR address = 0;
@@ -865,6 +866,14 @@ py_print_frame (PyObject *filter, frame_filter_flags flags,
|| print_what == LOC_AND_ADDRESS
|| print_what == SHORT_LOCATION));
+ if (!selected_frame.is_null ())
+ {
+ if (frame == selected_frame)
+ out->text ("* ");
+ else
+ out->text (" ");
+ }
+
/* Print frame level. MI does not require the level if
locals/variables only are being printed. */
if (print_level
@@ -1068,7 +1077,7 @@ py_print_frame (PyObject *filter, frame_filter_flags flags,
enum ext_lang_bt_status success
= py_print_frame (item, flags, args_type, out, indent,
- levels_printed);
+ levels_printed, selected_frame);
if (success == EXT_LANG_BT_ERROR)
return EXT_LANG_BT_ERROR;
@@ -1142,7 +1151,8 @@ enum ext_lang_bt_status
gdbpy_apply_frame_filter (const struct extension_language_defn *extlang,
const frame_info_ptr &frame, frame_filter_flags flags,
enum ext_lang_frame_args args_type,
- struct ui_out *out, int frame_low, int frame_high)
+ struct ui_out *out, int frame_low, int frame_high,
+ const frame_info_ptr &selected_frame)
{
struct gdbarch *gdbarch = NULL;
enum ext_lang_bt_status success = EXT_LANG_BT_ERROR;
@@ -1234,7 +1244,7 @@ gdbpy_apply_frame_filter (const struct extension_language_defn *extlang,
try
{
success = py_print_frame (item.get (), flags, args_type, out, 0,
- levels_printed);
+ levels_printed, selected_frame);
}
catch (const gdb_exception_error &except)
{
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 10c984bad4b..9245a4e25d7 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -444,7 +444,8 @@ extern enum ext_lang_bt_status gdbpy_apply_frame_filter
(const struct extension_language_defn *,
const frame_info_ptr &frame, frame_filter_flags flags,
enum ext_lang_frame_args args_type,
- struct ui_out *out, int frame_low, int frame_high);
+ struct ui_out *out, int frame_low, int frame_high,
+ const frame_info_ptr &selected_frame);
extern void gdbpy_preserve_values (const struct extension_language_defn *,
struct objfile *objfile,
copied_types_hash_t &copied_types);
diff --git a/gdb/stack.c b/gdb/stack.c
index 7329430adab..ab1fe33a72a 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -227,7 +227,8 @@ static void print_frame (struct ui_out *uiout,
const frame_print_options &opts,
const frame_info_ptr &frame, int print_level,
enum print_what print_what, int print_args,
- struct symtab_and_line sal);
+ struct symtab_and_line sal,
+ const frame_info_ptr &selected_frame);
static frame_info_ptr find_frame_for_function (const char *);
static frame_info_ptr find_frame_for_address (CORE_ADDR);
@@ -362,7 +363,7 @@ print_stack_frame (const frame_info_ptr &frame, int print_level,
{
print_frame_info (user_frame_print_options,
frame, print_level, print_what, 1 /* print_args */,
- set_current_sal);
+ set_current_sal, {});
if (set_current_sal)
set_current_sal_from_frame (frame);
}
@@ -961,7 +962,7 @@ static void
do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts,
const frame_info_ptr &frame, int print_level,
enum print_what print_what, int print_args,
- int set_current_sal)
+ int set_current_sal, const frame_info_ptr &selected_frame)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
int source_print;
@@ -983,6 +984,14 @@ do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts,
annotate_frame_begin (print_level ? frame_relative_level (frame) : 0,
gdbarch, get_frame_pc (frame));
+ if (!selected_frame.is_null ())
+ {
+ if (frame == selected_frame)
+ uiout->text ("* ");
+ else
+ uiout->text (" ");
+ }
+
/* Do this regardless of SOURCE because we don't have any source
to list for this frame. */
if (print_level)
@@ -1042,7 +1051,7 @@ do_print_frame_info (struct ui_out *uiout, const frame_print_options &fp_opts,
|| print_what == SHORT_LOCATION);
if (location_print || !sal.symtab)
print_frame (uiout, fp_opts, frame, print_level,
- print_what, print_args, sal);
+ print_what, print_args, sal, selected_frame);
source_print = (print_what == SRC_LINE || print_what == SRC_AND_LOC);
@@ -1129,11 +1138,11 @@ void
print_frame_info (const frame_print_options &fp_opts,
const frame_info_ptr &frame, int print_level,
enum print_what print_what, int print_args,
- int set_current_sal)
+ int set_current_sal, const frame_info_ptr &selected_frame)
{
do_with_buffered_output (do_print_frame_info, current_uiout,
fp_opts, frame, print_level, print_what,
- print_args, set_current_sal);
+ print_args, set_current_sal, selected_frame);
}
/* See stack.h. */
@@ -1263,7 +1272,8 @@ print_frame (struct ui_out *uiout,
const frame_print_options &fp_opts,
const frame_info_ptr &frame, int print_level,
enum print_what print_what, int print_args,
- struct symtab_and_line sal)
+ struct symtab_and_line sal,
+ const frame_info_ptr &selected_frame)
{
struct gdbarch *gdbarch = get_frame_arch (frame);
enum language funlang = language_unknown;
@@ -1282,6 +1292,14 @@ print_frame (struct ui_out *uiout,
{
ui_out_emit_tuple tuple_emitter (uiout, "frame");
+ if (!selected_frame.is_null ())
+ {
+ if (frame == selected_frame)
+ uiout->text ("* ");
+ else
+ uiout->text (" ");
+ }
+
if (print_level)
{
uiout->text ("#");
@@ -1930,6 +1948,8 @@ backtrace_command_1 (const frame_print_options &fp_opts,
if (fp_opts.print_raw_frame_arguments)
flags |= PRINT_RAW_FRAME_ARGUMENTS;
+ frame_info_ptr selected_frame = get_selected_frame ();
+
if (!bt_opts.no_filters)
{
enum ext_lang_frame_args arg_type;
@@ -1951,7 +1971,8 @@ backtrace_command_1 (const frame_print_options &fp_opts,
result = apply_ext_lang_frame_filter (get_current_frame (), flags,
arg_type, current_uiout,
- py_start, py_end);
+ py_start, py_end,
+ selected_frame);
}
/* Run the inbuilt backtrace if there are no filters registered, or
@@ -1982,7 +2003,7 @@ backtrace_command_1 (const frame_print_options &fp_opts,
hand, perhaps the code does or could be fixed to make sure
the frame->prev field gets set to NULL in that case). */
- print_frame_info (fp_opts, fi, 1, LOCATION, 1, 0);
+ print_frame_info (fp_opts, fi, 1, LOCATION, 1, 0, selected_frame);
if ((flags & PRINT_LOCALS) != 0)
print_frame_local_vars (fi, false, NULL, NULL, 1, gdb_stdout);
--
2.49.0
next reply other threads:[~2026-05-29 18:45 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-29 18:45 Tom Tromey [this message]
2026-05-29 19:05 ` Simon Marchi
2026-06-01 7:05 ` Schimpe, Christina
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=20260529184503.3263361-1-tom@tromey.com \
--to=tom@tromey.com \
--cc=gdb-patches@sourceware.org \
/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