Skip to content

Commit 901682e

Browse files
committedMay 30, 2023
gdb: add support for %V to printf command
This commit adds a new format for the printf and dprintf commands: '%V'. This new format takes any GDB expression and formats it as a string, just as GDB would for a 'print' command, e.g.: (gdb) print a1 $a = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} (gdb) printf "%V\n", a1 {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} (gdb) It is also possible to pass the same options to %V as you might pass to the print command, e.g.: (gdb) print -elements 3 -- a1 $4 = {2, 4, 6...} (gdb) printf "%V[-elements 3]\n", a1 {2, 4, 6...} (gdb) This new feature would effectively replace an existing feature of GDB, the $_as_string builtin convenience function. However, the $_as_string function has a few problems which this new feature solves: 1. $_as_string doesn't currently work when the inferior is not running, e.g: (gdb) printf "%s", $_as_string(a1) You can't do that without a process to debug. (gdb) The reason for this is that $_as_string returns a value object with string type. When we try to print this we call value_as_address, which ends up trying to push the string into the inferior's address space. Clearly we could solve this problem, the string data exists in GDB, so there's no reason why we have to push it into the inferior, but this is an existing problem that would need solving. 2. $_as_string suffers from the fact that C degrades arrays to pointers, e.g.: (gdb) printf "%s\n", $_as_string(a1) 0x404260 <a1> (gdb) The implementation of $_as_string is passed a gdb.Value object that is a pointer, it doesn't understand that it's actually an array. Solving this would be harder than issue #1 I think. The whole array to pointer transformation is part of our expression evaluation. And in most cases this is exactly what we want. It's not clear to me how we'd (easily) tell GDB that we didn't want this reduction in _some_ cases. But I'm sure this is solvable if we really wanted to. 3. $_as_string is a gdb.Function sub-class, and as such is passed gdb.Value objects. There's no super convenient way to pass formatting options to $_as_string. By this I mean that the new %V feature supports print formatting options. Ideally, we might want to add this feature to $_as_string, we might imagine it working something like: (gdb) printf "%s\n", $_as_string(a1, elements = 3, array_indexes = True) where the first item is the value to print, while the remaining options are the print formatting options. However, this relies on Python calling syntax, which isn't something that convenience functions handle. We could possibly rely on strictly positional arguments, like: (gdb) printf "%s\n", $_as_string(a1, 3, 1) But that's clearly terrible as there's far more print formatting options, and if you needed to set the 9th option you'd need to fill in all the previous options. And right now, the only way to pass these options to a gdb.Function is to have GDB first convert them all into gdb.Value objects, which is really overkill for what we want. The new %V format solves all these problems: the string is computed and printed entirely on the GDB side, we are able to print arrays as actual arrays rather than pointers, and we can pass named format arguments. Finally, the $_as_string is sold in the manual as allowing users to print the string representation of flag enums, so given: enum flags { FLAG_A = (1 << 0), FLAG_B = (1 << 1), FLAG_C = (1 << 1) }; enum flags ff = FLAG_B; We can: (gdb) printf "%s\n", $_as_string(ff) FLAG_B This works just fine with %V too: (gdb) printf "%V\n", ff FLAG_B So all functionality of $_as_string is replaced by %V. I'm not proposing to remove $_as_string, there might be users currently depending on it, but I am proposing that we don't push $_as_string in the documentation. As %V is a feature of printf, GDB's dprintf breakpoints naturally gain access to this feature too. dprintf breakpoints can be operated in three different styles 'gdb' (use GDB's printf), 'call' (call a function in the inferior), or 'agent' (perform the dprintf on the remote). The use of '%V' will work just fine when dprintf-style is 'gdb'. When dprintf-style is 'call' the format string and arguments are passed to an inferior function (printf by default). In this case GDB doesn't prevent use of '%V', but the documentation makes it clear that support for '%V' will depend on the inferior function being called. I chose this approach because the current implementation doesn't place any restrictions on the format string when operating in 'call' style. That is, the user might already be calling a function that supports custom print format specifiers (maybe including '%V') so, I claim, it would be wrong to block use of '%V' in this case. The documentation does make it clear that users shouldn't expect this to "just work" though. When dprintf-style is 'agent' then GDB does no support the use of '%V' (right now). This is handled at the point when GDB tries to process the format string and send the dprintf command to the remote, here's an example: Reading symbols from /tmp/hello.x... (gdb) dprintf call_me, "%V", a1 Dprintf 1 at 0x401152: file /tmp/hello.c, line 8. (gdb) set sysroot / (gdb) target remote | gdbserver --once - /tmp/hello.x Remote debugging using | gdbserver --once - /tmp/hello.x stdin/stdout redirected Process /tmp/hello.x created; pid = 3088822 Remote debugging using stdio Reading symbols from /lib64/ld-linux-x86-64.so.2... (No debugging symbols found in /lib64/ld-linux-x86-64.so.2) 0x00007ffff7fd3110 in _start () from /lib64/ld-linux-x86-64.so.2 (gdb) set dprintf-style agent (gdb) c Continuing. Unrecognized format specifier 'V' in printf Command aborted. (gdb) This is exactly how GDB would handle any other invalid format specifier, for example: Reading symbols from /tmp/hello.x... (gdb) dprintf call_me, "%Q", a1 Dprintf 1 at 0x401152: file /tmp/hello.c, line 8. (gdb) set sysroot / (gdb) target remote | gdbserver --once - /tmp/hello.x Remote debugging using | gdbserver --once - /tmp/hello.x stdin/stdout redirected Process /tmp/hello.x created; pid = 3089193 Remote debugging using stdio Reading symbols from /lib64/ld-linux-x86-64.so.2... (No debugging symbols found in /lib64/ld-linux-x86-64.so.2) 0x00007ffff7fd3110 in _start () from /lib64/ld-linux-x86-64.so.2 (gdb) set dprintf-style agent (gdb) c Continuing. Unrecognized format specifier 'Q' in printf Command aborted. (gdb) The error message isn't the greatest, but improving that can be put off for another day I hope. Reviewed-By: Eli Zaretskii <eliz@gnu.org> Acked-By: Simon Marchi <simon.marchi@efficios.com>
1 parent ec517d1 commit 901682e

File tree

7 files changed

+158
-11
lines changed

7 files changed

+158
-11
lines changed
 

‎gdb/NEWS

+11
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@
6767
break foo thread 1 task 1
6868
watch var thread 2 task 3
6969

70+
* The printf command now accepts a '%V' output format which will
71+
format an expression just as the 'print' command would. Print
72+
options can be placed withing '[...]' after the '%V' to modify how
73+
the value is printed. E.g:
74+
printf "%V", some_array
75+
printf "%V[-array-indexes on]", some_array
76+
will print the array without, or with array indexes included, just
77+
as the array would be printed by the 'print' command. This
78+
functionality is also available for dprintf when dprintf-style is
79+
'gdb'.
80+
7081
* New commands
7182

7283
maintenance print record-instruction [ N ]

‎gdb/doc/gdb.texinfo

+50-5
Original file line numberDiff line numberDiff line change
@@ -5962,18 +5962,29 @@ explicitly-supplied command lists.)
59625962
@table @code
59635963
@item gdb
59645964
@kindex dprintf-style gdb
5965-
Handle the output using the @value{GDBN} @code{printf} command.
5965+
Handle the output using the @value{GDBN} @code{printf} command. When
5966+
using this style, it is possible to use the @samp{%V} format specifier
5967+
(@pxref{%V Format Specifier}).
59665968

59675969
@item call
59685970
@kindex dprintf-style call
59695971
Handle the output by calling a function in your program (normally
5970-
@code{printf}).
5972+
@code{printf}). When using this style the supported format specifiers
5973+
depend entirely on the function being called.
5974+
5975+
Most of @value{GDB}'s format specifiers align with those supported by
5976+
the @code{printf} function, however, @value{GDB}'s @samp{%V} format
5977+
specifier extension is not supported by @code{printf}. When using
5978+
@samp{call} style dprintf, care should be taken to ensure that only
5979+
format specifiers supported by the output function are used, otherwise
5980+
the results will be undefined.
59715981

59725982
@item agent
59735983
@kindex dprintf-style agent
5974-
Have the remote debugging agent (such as @code{gdbserver}) handle
5975-
the output itself. This style is only available for agents that
5976-
support running commands on the target.
5984+
Have the remote debugging agent (such as @code{gdbserver}) handle the
5985+
output itself. This style is only available for agents that support
5986+
running commands on the target. This style does not support the
5987+
@samp{%V} format specifier.
59775988
@end table
59785989

59795990
@item set dprintf-function @var{function}
@@ -13141,6 +13152,10 @@ frame specified by @var{number_of_frames}.
1314113152

1314213153
@findex $_as_string@r{, convenience function}
1314313154
@item $_as_string(@var{value})
13155+
This convenience function is considered deprecated, and could be
13156+
removed from future versions of @value{GDBN}. Use the @samp{%V} format
13157+
specifier instead (@pxref{%V Format Specifier}).
13158+
1314413159
Return the string representation of @var{value}.
1314513160

1314613161
This function is useful to obtain the textual label (enumerator) of an
@@ -29051,6 +29066,36 @@ Here's an example of printing DFP types using the above conversion letters:
2905129066
printf "D32: %Hf - D64: %Df - D128: %DDf\n",1.2345df,1.2E10dd,1.2E1dl
2905229067
@end smallexample
2905329068

29069+
@anchor{%V Format Specifier}
29070+
Additionally, @code{printf} supports a special @samp{%V} output format.
29071+
This format prints the string representation of an expression just as
29072+
@value{GDBN} would produce with the standard @kbd{print} command
29073+
(@pxref{Data, ,Examining Data}):
29074+
29075+
@smallexample
29076+
(@value{GDBP}) print array
29077+
$1 = @{0, 1, 2, 3, 4, 5@}
29078+
(@value{GDBP}) printf "Array is: %V\n", array
29079+
Array is: @{0, 1, 2, 3, 4, 5@}
29080+
@end smallexample
29081+
29082+
It is possible to include print options with the @samp{%V} format by
29083+
placing them in @samp{[...]} immediately after the @samp{%V}, like
29084+
this:
29085+
29086+
@smallexample
29087+
(@value{GDBP}) printf "Array is: %V[-array-indexes on]\n", array
29088+
Array is: @{[0] = 0, [1] = 1, [2] = 2, [3] = 3, [4] = 4, [5] = 5@}
29089+
@end smallexample
29090+
29091+
If you need to print a literal @samp{[} directly after a @samp{%V}, then
29092+
just include an empty print options list:
29093+
29094+
@smallexample
29095+
(@value{GDBP}) printf "Array is: %V[][Hello]\n", array
29096+
Array is: @{0, 1, 2, 3, 4, 5@}[Hello]
29097+
@end smallexample
29098+
2905429099
@anchor{eval}
2905529100
@kindex eval
2905629101
@item eval @var{template}, @var{expressions}@dots{}

‎gdb/printcmd.c

+29-1
Original file line numberDiff line numberDiff line change
@@ -2735,7 +2735,7 @@ ui_printf (const char *arg, struct ui_file *stream)
27352735
if (*s++ != '"')
27362736
error (_("Bad format string, missing '\"'."));
27372737

2738-
format_pieces fpieces (&s);
2738+
format_pieces fpieces (&s, false, true);
27392739

27402740
if (*s++ != '"')
27412741
error (_("Bad format string, non-terminated '\"'."));
@@ -2877,6 +2877,34 @@ ui_printf (const char *arg, struct ui_file *stream)
28772877
case ptr_arg:
28782878
printf_pointer (stream, current_substring, val_args[i]);
28792879
break;
2880+
case value_arg:
2881+
{
2882+
value_print_options print_opts;
2883+
get_user_print_options (&print_opts);
2884+
2885+
if (current_substring[2] == '[')
2886+
{
2887+
std::string args (&current_substring[3],
2888+
strlen (&current_substring[3]) - 1);
2889+
2890+
const char *args_ptr = args.c_str ();
2891+
2892+
/* Override global settings with explicit options, if
2893+
any. */
2894+
auto group
2895+
= make_value_print_options_def_group (&print_opts);
2896+
gdb::option::process_options
2897+
(&args_ptr, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR,
2898+
group);
2899+
2900+
if (*args_ptr != '\0')
2901+
error (_("unexpected content in print options: %s"),
2902+
args_ptr);
2903+
}
2904+
2905+
print_formatted (val_args[i], 0, &print_opts, stream);
2906+
}
2907+
break;
28802908
case literal_piece:
28812909
/* Print a portion of the format string that has no
28822910
directives. Note that this will not include any

‎gdb/testsuite/gdb.base/printcmds.c

+13
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ enum flag_enum
108108
FE_TWO_LEGACY = 0x02,
109109
};
110110

111+
enum flag_enum one = FE_ONE;
111112
enum flag_enum three = (enum flag_enum) (FE_ONE | FE_TWO);
112113

113114
/* Another enum considered as a "flag enum", but with no enumerator with value
@@ -152,6 +153,18 @@ struct some_struct
152153
}
153154
};
154155

156+
/* This is used in the printf test. */
157+
struct small_struct
158+
{
159+
int a;
160+
int b;
161+
int c;
162+
} a_small_struct = {
163+
1,
164+
2,
165+
3
166+
};
167+
155168
/* The following variables are used for testing byte repeat sequences.
156169
The variable names are encoded: invalid_XYZ where:
157170
X = start

‎gdb/testsuite/gdb.base/printcmds.exp

+27-1
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,32 @@ proc test_printf_with_strings {} {
981981
}
982982
}
983983

984+
# Test the printf '%V' format.
985+
proc test_printf_V_format {} {
986+
# Enums.
987+
gdb_test {printf "%V\n", one} "FE_ONE"
988+
gdb_test {printf "%V\n", three} "\\(FE_ONE \\| FE_TWO\\)"
989+
gdb_test {printf "%V\n", flag_enum_without_zero} "0"
990+
gdb_test {printf "%V\n", three_not_flag} "3"
991+
992+
# Arrays.
993+
gdb_test {printf "%V\n", a1} "\\{2, 4, 6, 8, 10, 12, 14, 16, 18, 20\\}"
994+
gdb_test {printf "%V[]\n", a1} "\\{2, 4, 6, 8, 10, 12, 14, 16, 18, 20\\}"
995+
gdb_test {printf "%V[][]\n", a1} \
996+
"\\{2, 4, 6, 8, 10, 12, 14, 16, 18, 20\\}\\\[\\\]"
997+
gdb_test {printf "%V[-elements 3]\n", a1} "\\{2, 4, 6\\.\\.\\.\\}"
998+
gdb_test {printf "%V[-elements 3][]\n", a1} \
999+
"\\{2, 4, 6\\.\\.\\.\\}\\\[\\\]"
1000+
gdb_test {printf "%V[-elements 3 -array-indexes on]\n", a1} \
1001+
"\\{\\\[0\\\] = 2, \\\[1\\\] = 4, \\\[2\\\] = 6\\.\\.\\.\\}"
1002+
1003+
# Structures.
1004+
gdb_test {printf "%V\n", a_small_struct} \
1005+
"\\{a = 1, b = 2, c = 3\\}"
1006+
gdb_test {printf "%V[-pretty on]\n", a_small_struct} \
1007+
"\\{\r\n a = 1,\r\n b = 2,\r\n c = 3\r\n\\}"
1008+
}
1009+
9841010
proc test_print_symbol {} {
9851011
gdb_test_no_output "set print symbol on"
9861012

@@ -1127,7 +1153,6 @@ proc test_printf_convenience_var {prefix} {
11271153
}
11281154
}
11291155

1130-
11311156
clean_restart
11321157

11331158
gdb_test "print \$pc" "No registers\\."
@@ -1213,6 +1238,7 @@ test_print_enums
12131238
test_printf
12141239
test_printf_with_dfp
12151240
test_printf_with_strings
1241+
test_printf_V_format
12161242
test_print_symbol
12171243
test_repeat_bytes
12181244
test_radices

‎gdbsupport/format.cc

+24-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
#include "common-defs.h"
2121
#include "format.h"
2222

23-
format_pieces::format_pieces (const char **arg, bool gdb_extensions)
23+
format_pieces::format_pieces (const char **arg, bool gdb_extensions,
24+
bool value_extension)
2425
{
2526
const char *s;
2627
const char *string;
@@ -44,7 +45,7 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
4445
char *f = (char *) alloca (strlen (s) + 1);
4546
string = f;
4647

47-
while ((gdb_extensions || *s != '"') && *s != '\0')
48+
while (*s != '"' && *s != '\0')
4849
{
4950
int c = *s++;
5051
switch (c)
@@ -340,6 +341,27 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
340341
bad = 1;
341342
break;
342343

344+
case 'V':
345+
if (!value_extension)
346+
error (_("Unrecognized format specifier '%c' in printf"), *f);
347+
348+
if (lcount > 1 || seen_h || seen_big_h || seen_big_h
349+
|| seen_big_d || seen_double_big_d || seen_size_t
350+
|| seen_prec || seen_zero || seen_space || seen_plus)
351+
bad = 1;
352+
353+
this_argclass = value_arg;
354+
355+
if (f[1] == '[')
356+
{
357+
/* Move F forward to the next ']' character if such a
358+
character exists, otherwise leave F unchanged. */
359+
const char *tmp = strchr (f, ']');
360+
if (tmp != nullptr)
361+
f = tmp;
362+
}
363+
break;
364+
343365
case '*':
344366
error (_("`*' not supported for precision or width in printf"));
345367

‎gdbsupport/format.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ enum argclass
4141
int_arg, long_arg, long_long_arg, size_t_arg, ptr_arg,
4242
string_arg, wide_string_arg, wide_char_arg,
4343
double_arg, long_double_arg,
44-
dec32float_arg, dec64float_arg, dec128float_arg
44+
dec32float_arg, dec64float_arg, dec128float_arg,
45+
value_arg
4546
};
4647

4748
/* A format piece is a section of the format string that may include a
@@ -75,7 +76,8 @@ class format_pieces
7576
{
7677
public:
7778

78-
format_pieces (const char **arg, bool gdb_extensions = false);
79+
format_pieces (const char **arg, bool gdb_extensions = false,
80+
bool value_extension = false);
7981
~format_pieces () = default;
8082

8183
DISABLE_COPY_AND_ASSIGN (format_pieces);

0 commit comments

Comments
 (0)
Please sign in to comment.