From patchwork Fri Feb 5 14:17:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Marchi X-Patchwork-Id: 10740 Received: (qmail 122292 invoked by alias); 5 Feb 2016 14:18:09 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 122222 invoked by uid 89); 5 Feb 2016 14:18:08 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.2 required=5.0 tests=BAYES_00, SPF_SOFTFAIL autolearn=no version=3.3.2 spammy=first, Output, Hx-spam-relays-external:sk:smtp.el, H*RU:sk:smtp.el X-HELO: smtp.electronicbox.net Received: from smtp.electronicbox.net (HELO smtp.electronicbox.net) (96.127.255.82) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 05 Feb 2016 14:17:59 +0000 Received: from localhost.localdomain (cable-192.222.137.139.electronicbox.net [192.222.137.139]) by smtp.electronicbox.net (Postfix) with ESMTP id C7B6B440E7B; Fri, 5 Feb 2016 09:17:57 -0500 (EST) From: Simon Marchi To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH] Add printf format specifier for printing enumerator Date: Fri, 5 Feb 2016 09:17:56 -0500 Message-Id: <1454681876-17628-1-git-send-email-simon.marchi@polymtl.ca> This patch adds a format specifier to gdb's printf command, to print the enumerator (the text label) of an enum value. It's currently possible to display that value using the print command, but not as part of a formatted string. For example, assuming we have the following enum definition: enum NodeType { ... NODE_INTEGER, ... }; enum NodeType node = NODE_INTEGER; we can display it this way: (gdb) printf "Visiting node of type %q\n", node Visiting node of type NODE_INTEGER All the letters of "enum" are already used as format specifiers. Actually, %m is used in GNU's extensions to printf to print errno's text representation. It is not used in GDB's printf, but I'd prefer to leave the door open if we want to add it one day. I just chose %q at random, if you think another letter would be a better choice, I have no objection. In the tests, I added some enum values to enum some_volatile_enum, so that it's not treated as an enum flag. What do you think? gdb/ChangeLog: * common/format.c (parse_format_string): Handle 'q' format specifier. * common/format.h (enum argclass): Add enum_arg. * printcmd.c (printf_enum): New function. (ui_printf): Handle enum_arg argclass. * valprint.c (val_print_enum_label): New function, factored out from... (generic_val_print_enum): ... this. * valprint.h (val_print_enum_label): New function declaration. * NEWS: Mention the new format specifier. gdb/testsuite/ChangeLog: * gdb.base/printcmds.exp (test_printf): Add enum printf tests. gdb/doc/ChangeLog: * gdb.texinfo (Commands for Controlled Output): Mention q conversion letter. --- gdb/NEWS | 6 +++ gdb/common/format.c | 4 ++ gdb/common/format.h | 2 +- gdb/doc/gdb.texinfo | 3 ++ gdb/printcmd.c | 20 +++++++++ gdb/testsuite/gdb.base/printcmds.c | 2 +- gdb/testsuite/gdb.base/printcmds.exp | 10 +++++ gdb/valprint.c | 87 +++++++++++++++++++----------------- gdb/valprint.h | 3 ++ 9 files changed, 95 insertions(+), 42 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 962eae4..ce9f7b0 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,12 @@ *** Changes since GDB 7.10 +* GDB's printf command now has a format specifier (%q) to display the + textual label (enumerator) of an enumeration value. For example: + + (gdb) printf "Visiting node of type %q\n", node + Visiting node of type NODE_INTEGER + * GDB now supports debugging kernel-based threads on FreeBSD. * Per-inferior thread numbers diff --git a/gdb/common/format.c b/gdb/common/format.c index fbb82fd..f90893e 100644 --- a/gdb/common/format.c +++ b/gdb/common/format.c @@ -294,6 +294,10 @@ parse_format_string (const char **arg) case '\0': error (_("Incomplete format specifier at end of format string")); + case 'q': + this_argclass = enum_arg; + break; + default: error (_("Unrecognized format specifier '%c' in printf"), *f); } diff --git a/gdb/common/format.h b/gdb/common/format.h index cb56a2c..884403e 100644 --- a/gdb/common/format.h +++ b/gdb/common/format.h @@ -35,7 +35,7 @@ enum argclass literal_piece, int_arg, long_arg, long_long_arg, ptr_arg, string_arg, wide_string_arg, wide_char_arg, - double_arg, long_double_arg, decfloat_arg + double_arg, long_double_arg, decfloat_arg, enum_arg }; /* A format piece is a section of the format string that may include a diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 2d09d13..183bd9e 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -24343,6 +24343,9 @@ sequences, such as @code{\n}, @samp{\t}, @samp{\\}, @samp{\"}, single character. Octal and hexadecimal escape sequences are not supported. +@code{printf} supports the @samp{q} conversion letter to print the textual +label (enumerator) of a C enumeration value. + Additionally, @code{printf} supports conversion specifications for DFP (@dfn{Decimal Floating Point}) types using the following length modifiers together with a floating point specifier. diff --git a/gdb/printcmd.c b/gdb/printcmd.c index f5c4211..e4637df 100644 --- a/gdb/printcmd.c +++ b/gdb/printcmd.c @@ -2258,6 +2258,23 @@ printf_pointer (struct ui_file *stream, const char *format, } } +static void +printf_enum (struct ui_file *stream, struct value *value) +{ + struct type *type = check_typedef (value_type (value)); + + if (TYPE_CODE (type) == TYPE_CODE_ENUM) + { + val_print_enum_label (type, value_contents_for_printing (value), + value_embedded_offset (value), stream); + } + else + { + error (_("\ +Value given for enum format specifier (%%q) is not of an enum type.")); + } +} + /* printf "printf format string" ARG to STREAM. */ static void @@ -2446,6 +2463,9 @@ ui_printf (const char *arg, struct ui_file *stream) case ptr_arg: printf_pointer (stream, current_substring, val_args[i]); break; + case enum_arg: + printf_enum (stream, val_args[i]); + break; case literal_piece: /* Print a portion of the format string that has no directives. Note that this will not include any diff --git a/gdb/testsuite/gdb.base/printcmds.c b/gdb/testsuite/gdb.base/printcmds.c index 57e04e6..bc7c981 100644 --- a/gdb/testsuite/gdb.base/printcmds.c +++ b/gdb/testsuite/gdb.base/printcmds.c @@ -90,7 +90,7 @@ struct some_arrays { struct some_arrays *parrays = &arrays; -enum some_volatile_enum { enumvolval1, enumvolval2 }; +enum some_volatile_enum { enumvolval1, enumvolval2, enumvolval3, enumvolval4 }; /* A volatile enum variable whose name is the same as the enumeration name. See PR11827. */ diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp index 3c78a53..4254581d 100644 --- a/gdb/testsuite/gdb.base/printcmds.exp +++ b/gdb/testsuite/gdb.base/printcmds.exp @@ -763,6 +763,16 @@ proc test_printf {} { "" \ "create hibob command" gdb_test "hibob" "hi bob zzz.*y" "run hibob command" + + # Test for enum printing (%q) in printf + gdb_test "printf \"%q\\n\", some_volatile_enum" "enumvolval1" + gdb_test "printf \"%q\\n\", enumvolval2" "enumvolval2" + gdb_test "printf \"%q\\n\", (enum some_volatile_enum) 1" "enumvolval2" + gdb_test "printf \"%q\\n\", (enum some_volatile_enum) 111" "111" + gdb_test "printf \"%q\\n\", three" "\\(ONE | TWO\\)" + gdb_test "printf \"%q\\n\", (enum flag_enum) 8" "\\(unknown: 8\\)" + + gdb_test "printf \"%q\\n\", 2" "Value given for enum format specifier \\(%q\\) is not of an enum type." } #Test printing DFP values with printf diff --git a/gdb/valprint.c b/gdb/valprint.c index a9b03ec..fde48b4 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -526,35 +526,23 @@ generic_val_print_ref (struct type *type, const gdb_byte *valaddr, } } -/* generic_val_print helper for TYPE_CODE_ENUM. */ - -static void -generic_val_print_enum (struct type *type, const gdb_byte *valaddr, - int embedded_offset, struct ui_file *stream, - const struct value *original_value, - const struct value_print_options *options) +void +val_print_enum_label (struct type *type, const gdb_byte *valaddr, + int embedded_offset, struct ui_file *stream) { - unsigned int i; - unsigned int len; - LONGEST val; + int len = TYPE_NFIELDS (type); + int i; struct gdbarch *gdbarch = get_type_arch (type); int unit_size = gdbarch_addressable_memory_unit_size (gdbarch); + LONGEST val = unpack_long (type, valaddr + embedded_offset * unit_size); - if (options->format) - { - val_print_scalar_formatted (type, valaddr, embedded_offset, - original_value, options, 0, stream); - return; - } - len = TYPE_NFIELDS (type); - val = unpack_long (type, valaddr + embedded_offset * unit_size); for (i = 0; i < len; i++) { QUIT; if (val == TYPE_FIELD_ENUMVAL (type, i)) - { - break; - } + { + break; + } } if (i < len) { @@ -565,31 +553,31 @@ generic_val_print_enum (struct type *type, const gdb_byte *valaddr, int first = 1; /* We have a "flag" enum, so we try to decompose it into - pieces as appropriate. A flag enum has disjoint - constants by definition. */ + pieces as appropriate. A flag enum has disjoint + constants by definition. */ fputs_filtered ("(", stream); for (i = 0; i < len; ++i) - { - QUIT; + { + QUIT; - if ((val & TYPE_FIELD_ENUMVAL (type, i)) != 0) - { - if (!first) - fputs_filtered (" | ", stream); - first = 0; + if ((val & TYPE_FIELD_ENUMVAL (type, i)) != 0) + { + if (!first) + fputs_filtered (" | ", stream); + first = 0; - val &= ~TYPE_FIELD_ENUMVAL (type, i); - fputs_filtered (TYPE_FIELD_NAME (type, i), stream); - } - } + val &= ~TYPE_FIELD_ENUMVAL (type, i); + fputs_filtered (TYPE_FIELD_NAME (type, i), stream); + } + } if (first || val != 0) - { - if (!first) - fputs_filtered (" | ", stream); - fputs_filtered ("unknown: ", stream); - print_longest (stream, 'd', 0, val); - } + { + if (!first) + fputs_filtered (" | ", stream); + fputs_filtered ("unknown: ", stream); + print_longest (stream, 'd', 0, val); + } fputs_filtered (")", stream); } @@ -597,6 +585,25 @@ generic_val_print_enum (struct type *type, const gdb_byte *valaddr, print_longest (stream, 'd', 0, val); } +/* generic_val_print helper for TYPE_CODE_ENUM. */ + +static void +generic_val_print_enum (struct type *type, const gdb_byte *valaddr, + int embedded_offset, struct ui_file *stream, + const struct value *original_value, + const struct value_print_options *options) +{ + if (options->format) + { + val_print_scalar_formatted (type, valaddr, embedded_offset, + original_value, options, 0, stream); + } + else + { + val_print_enum_label (type, valaddr, embedded_offset, stream); + } +} + /* generic_val_print helper for TYPE_CODE_FLAGS. */ static void diff --git a/gdb/valprint.h b/gdb/valprint.h index bb03024..97a22d5 100644 --- a/gdb/valprint.h +++ b/gdb/valprint.h @@ -170,6 +170,9 @@ extern void val_print_unavailable (struct ui_file *stream); extern void val_print_invalid_address (struct ui_file *stream); +extern void val_print_enum_label (struct type *type, const gdb_byte *valaddr, + int embedded_offset, struct ui_file *stream); + /* An instance of this is passed to generic_val_print and describes some language-specific ways to print things. */