[PATCH v2 8/9] compile: New compile printf

Jan Kratochvil
2015-04-06T17:27:36Z

Hi,

command naming needs to follow what gets decided for 'compile print'.

This part sends the output to inferior stdout, only the next patch will
redirect it to GDB (so that for example it works for remote gdbserver).

It cannot work for core files as one cannot execute inferior code there.
There were some ideas such as compiling the entered sources into GCC
intermediate form (GIMPLE?) and interpret it by GDB on top of the core file.
That would be much more complicated, this implementation is made according to
Phil's specification.

Besides existing
(gdb) set compile-args
there will be now also:
(gdb) set compile-printf-args
Maybe it would be worth to start some set sub-category 'compile' such as:
(gdb) set compile args
(gdb) set compile printf-args
That would mean the whole process of deprecating 'set compile-args' etc.

Jan

gdb/ChangeLog
2015-04-06 Jan Kratochvil <jan.kratochvil@redhat.com>

* compile/compile-c-support.c (add_code_header, add_code_footer)
(c_compute_program): Add COMPILE_I_PRINTF_SCOPE.
* compile/compile-object-load.c (get_regs_type): Verify 2nd parameter
type.
(compile_object_load): Add COMPILE_I_PRINTF_SCOPE.
* compile/compile.c (compile_print_command): Rename to ...
(compile_print): ... here.
(compile_print_command, compile_printf_command, compile_printf_args)
(compile_printf_args_argv, set_compile_printf_args)
(show_compile_printf_args): New.
(get_args): Add COMPILE_I_PRINTF_SCOPE support.
(_initialize_compile): Install compile_printf_command,
set_compile_printf_args and show_compile_printf_args. Set default
COMPILE_PRINTF_ARGS.
* defs.h (enum compile_i_scope_types): Add COMPILE_I_PRINTF_SCOPE.

gdb/doc/ChangeLog
2015-04-06 Jan Kratochvil <jan.kratochvil@redhat.com>

* gdb.texinfo (Compiling and Injecting Code): Add compile printf.

gdb/testsuite/ChangeLog
2015-04-06 Jan Kratochvil <jan.kratochvil@redhat.com>

* gdb.compile/compile-print.exp (compile printf "0x%x\n", varint)
(compile printf "0x%x\n"): New.

diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c
index 86b02cf..92c41b6 100644
--- a/gdb/compile/compile-c-support.c
+++ b/gdb/compile/compile-c-support.c
@@ -206,7 +206,17 @@ add_code_header (enum compile_i_scope_types type, struct ui_file *buf)
") {\n",
buf);
break;
-
+ case COMPILE_I_PRINTF_SCOPE:
+ fputs_unfiltered ("#include <stdio.h>\n"
+ "void "
+ GCC_FE_WRAPPER_FUNCTION
+ " (struct "
+ COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG
+ " *"
+ COMPILE_I_SIMPLE_REGISTER_ARG_NAME
+ ") {\n",
+ buf);
+ break;
case COMPILE_I_RAW_SCOPE:
break;
default:
@@ -226,6 +236,7 @@ add_code_footer (enum compile_i_scope_types type, struct ui_file *buf)
case COMPILE_I_SIMPLE_SCOPE:
case COMPILE_I_PRINT_ADDRESS_SCOPE:
case COMPILE_I_PRINT_VALUE_SCOPE:
+ case COMPILE_I_PRINTF_SCOPE:
fputs_unfiltered ("}\n", buf);
break;
case COMPILE_I_RAW_SCOPE:
@@ -390,7 +401,8 @@ c_compute_program (struct compile_instance *inst,

if (inst->scope == COMPILE_I_SIMPLE_SCOPE
|| inst->scope == COMPILE_I_PRINT_ADDRESS_SCOPE
- || inst->scope == COMPILE_I_PRINT_VALUE_SCOPE)
+ || inst->scope == COMPILE_I_PRINT_VALUE_SCOPE
+ || inst->scope == COMPILE_I_PRINTF_SCOPE)
{
ui_file_put (var_stream, ui_file_write_for_put, buf);
fputs_unfiltered ("#pragma GCC user_expression\n", buf);
@@ -418,6 +430,11 @@ c_compute_program (struct compile_instance *inst,
(inst->scope == COMPILE_I_PRINT_ADDRESS_SCOPE
? "&" : ""));
break;
+ case COMPILE_I_PRINTF_SCOPE:
+ fprintf_unfiltered (buf,
+"printf (%s);\n"
+ , input);
+ break;
default:
fputs_unfiltered (input, buf);
break;
diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c
index e67e5ba..bc74590 100644
--- a/gdb/compile/compile-object-load.c
+++ b/gdb/compile/compile-object-load.c
@@ -470,7 +470,7 @@ static struct type *
get_regs_type (struct symbol *func_sym, struct objfile *objfile)
{
struct type *func_type = SYMBOL_TYPE (func_sym);
- struct type *regsp_type, *regs_type;
+ struct type *regsp_type, *regs_type, *void_type, *voidp_type;

/* No register parameter present. */
if (TYPE_NFIELDS (func_type) == 0)
@@ -490,6 +490,18 @@ get_regs_type (struct symbol *func_sym, struct objfile *objfile)
TYPE_CODE (regs_type), GCC_FE_WRAPPER_FUNCTION,
objfile_name (objfile));

+ /* No register parameter present. */
+ if (TYPE_NFIELDS (func_type) == 1)
+ return regs_type;
+
+ void_type = builtin_type (target_gdbarch ())->builtin_void;
+ voidp_type = lookup_pointer_type (void_type);
+ if (!types_deeply_equal (voidp_type, TYPE_FIELD_TYPE (func_type, 1)))
+ error (_("Invalid type of function \"%s\" parameter \"%s\" in compiled "
+ "module \"%s\"."),
+ GCC_FE_WRAPPER_FUNCTION, COMPILE_I_PRINT_OUT_ARG,
+ objfile_name (objfile));
+
return regs_type;
}

@@ -637,6 +649,10 @@ compile_object_load (const char *object_file, const char *source_file,
expect_parameters = 2;
expect_return_type = builtin_type (target_gdbarch ())->builtin_void;
break;
+ case COMPILE_I_PRINTF_SCOPE:
+ expect_parameters = 1;
+ expect_return_type = builtin_type (target_gdbarch ())->builtin_void;
+ break;
default:
internal_error (__FILE__, __LINE__, _("invalid scope %d"), scope);
}
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index c4b8f2e..6bbe8df 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -174,17 +174,14 @@ compile_print_value (struct value *val, void *data_voidp)
print_value (val, fmtp);
}

-/* Handle the input from the 'compile print' command. The "compile
- print" command is used to evaluate and print an expression that may
- contain calls to the GCC compiler. The language expected in this
- compile command is the language currently set in GDB. */
+/* Implement 'compile print' and 'compile printf' command according
+ to SCOPE. */

static void
-compile_print_command (char *arg_param, int from_tty)
+compile_print (char *arg_param, enum compile_i_scope_types scope)
{
const char *arg = arg_param;
struct cleanup *cleanup;
- enum compile_i_scope_types scope = COMPILE_I_PRINT_ADDRESS_SCOPE;
struct format_data *fmtp;

cleanup = make_cleanup_restore_integer (&interpreter_async);
@@ -207,6 +204,25 @@ compile_print_command (char *arg_param, int from_tty)
do_cleanups (cleanup);
}

+/* Handle the input from the 'compile print' command. The "compile
+ print" command is used to evaluate and print an expression that may
+ contain calls to the GCC compiler. The language expected in this
+ compile command is the language currently set in GDB. */
+
+static void
+compile_print_command (char *arg, int from_tty)
+{
+ compile_print (arg, COMPILE_I_PRINT_ADDRESS_SCOPE);
+}
+
+/* Handle the input from the 'compile printf' command. */
+
+static void
+compile_printf_command (char *arg, int from_tty)
+{
+ compile_print (arg, COMPILE_I_PRINTF_SCOPE);
+}
+
/* A cleanup function to remove a directory and all its contents. */

static void
@@ -308,6 +324,14 @@ static char *compile_args;
static int compile_args_argc;
static char **compile_args_argv;

+/* String for 'set compile-printf-args' and 'show compile-printf-args'. */
+static char *compile_printf_args;
+
+/* Parsed form of COMPILE_PRINTF_ARGS.
+ COMPILE_PRINTF_ARGS_ARGV is NULL terminated. */
+static int compile_printf_args_argc;
+static char **compile_printf_args_argv;
+
/* Implement 'set compile-args'. */

static void
@@ -328,6 +352,27 @@ show_compile_args (struct ui_file *file, int from_tty,
value);
}

+/* Implement 'set compile-printf-args'. */
+
+static void
+set_compile_printf_args (char *args, int from_tty, struct cmd_list_element *c)
+{
+ freeargv (compile_printf_args_argv);
+ build_argc_argv (compile_printf_args,
+ &compile_printf_args_argc, &compile_printf_args_argv);
+}
+
+/* Implement 'show compile-printf-args'. */
+
+static void
+show_compile_printf_args (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Compile printf command command-line arguments "
+ "are \"%s\".\n"),
+ value);
+}
+
/* Append ARGC and ARGV (as parsed by build_argc_argv) to *ARGCP and *ARGVP.
ARGCP+ARGVP can be zero+NULL and also ARGC+ARGV can be zero+NULL. */

@@ -422,6 +467,10 @@ get_args (const struct compile_instance *compiler, struct gdbarch *gdbarch,
freeargv (argv_compiler);

append_args (argcp, argvp, compile_args_argc, compile_args_argv);
+
+ if (compiler->scope == COMPILE_I_PRINTF_SCOPE)
+ append_args (argcp, argvp,
+ compile_printf_args_argc, compile_printf_args_argv);
}

/* A cleanup function to destroy a gdb_gcc_instance. */
@@ -727,6 +776,24 @@ EXPR may be preceded with /FMT, where FMT is a format letter\n\
but no count or size letter (see \"x\" command)."),
&compile_command_list);

+ add_cmd ("printf", class_obscure, compile_printf_command,
+ _("\
+Print formatted output with the compiler.\n\
+\n\
+Usage: compile printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
+\n\
+The arguments may be specified on the same line as the command, e.g.:\n\
+\n\
+ compile printf \"%d\\n\", i\n\
+\n\
+Alternatively, you can type the arguments interactively.\n\
+You can invoke this mode when no argument is given to the command\n\
+(i.e., \"compile printf\" is typed with nothing after it). An\n\
+interactive prompt will be shown allowing you to enter multiple\n\
+lines of source code. Type a line containing \"end\" to indicate\n\
+the end of the source code."),
+ &compile_command_list);
+
add_setshow_boolean_cmd ("compile", class_maintenance, &compile_debug, _("\
Set compile command debugging."), _("\
Show compile command debugging."), _("\
@@ -744,6 +811,15 @@ String quoting is parsed like in shell, for example:\n\
-mno-align-double \"-I/dir with a space/include\""),
set_compile_args, show_compile_args, &setlist, &showlist);

+ add_setshow_string_cmd ("compile-printf-args", class_support,
+ &compile_printf_args,
+ _("Set compile command GCC command-line arguments FIXME"),
+ _("Show compile command GCC command-line arguments FIXME"),
+ _("\
+Use options like -Werror which get added for 'compile printf' command."),
+ set_compile_printf_args, show_compile_printf_args,
+ &setlist, &showlist);
+
/* Override flags possibly coming from DW_AT_producer. */
compile_args = xstrdup ("-O0 -gdwarf-4"
/* We use -fPIE Otherwise GDB would need to reserve space large enough for
@@ -762,4 +838,7 @@ String quoting is parsed like in shell, for example:\n\
" -fno-stack-protector"
);
set_compile_args (compile_args, 0, NULL);
+
+ compile_printf_args = xstrdup ("-Werror=format");
+ set_compile_printf_args (compile_printf_args, 0, NULL);
}
diff --git a/gdb/defs.h b/gdb/defs.h
index 8da7d24..0bcb7f0 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -73,9 +73,16 @@ enum compile_i_scope_types

/* A printable expression scope. Wrap an expression into a scope
suitable for the "compile print" command. It uses the generic
- function name "_gdb_expr". */
+ function name "_gdb_expr". COMPILE_I_PRINT_ADDRESS_SCOPE variant
+ is the usual one, taking address of the object.
+ COMPILE_I_PRINT_VALUE_SCOPE is needed for arrays where the array
+ name already specifies its address. */
COMPILE_I_PRINT_ADDRESS_SCOPE,
COMPILE_I_PRINT_VALUE_SCOPE,
+
+ /* Implement the "compile printf" command. SCOPE_DATA contains
+ string containing the format string together with its arguments. */
+ COMPILE_I_PRINTF_SCOPE,
};

/* Just in case they're not defined in stdio.h. */
diff --git a/gdb/testsuite/gdb.compile/compile-print.exp b/gdb/testsuite/gdb.compile/compile-print.exp
index 92c6240..14fd489 100644
--- a/gdb/testsuite/gdb.compile/compile-print.exp
+++ b/gdb/testsuite/gdb.compile/compile-print.exp
@@ -56,3 +56,8 @@ gdb_test "compile print/x 256" " = 0x100"
gdb_test {print $} " = 256"

gdb_test "compile print varobject" { = {field = 1}}
+
+if ![is_remote target] {
+ gdb_test {compile printf "0x%x\n", varint} "\r\n0xa"
+ gdb_test {compile printf "0x%x\n"} "\r\nCompilation failed\\."
+}
---
gdb/compile/compile-c-support.c | 21 ++++++
gdb/compile/compile-object-load.c | 18 +++++
gdb/compile/compile.c | 91 +++++++++++++++++++++++++--
gdb/defs.h | 4 +
gdb/doc/gdb.texinfo | 14 ++++
gdb/testsuite/gdb.compile/compile-print.exp | 5 +
6 files changed, 144 insertions(+), 9 deletions(-)

diff --git a/gdb/compile/compile-c-support.c b/gdb/compile/compile-c-support.c
index 86b02cf..92c41b6 100644
--- a/gdb/compile/compile-c-support.c
+++ b/gdb/compile/compile-c-support.c
@@ -206,7 +206,17 @@ add_code_header (enum compile_i_scope_types type, struct ui_file *buf)
") {\n",
buf);
break;
-
+ case COMPILE_I_PRINTF_SCOPE:
+ fputs_unfiltered ("#include <stdio.h>\n"
+ "void "
+ GCC_FE_WRAPPER_FUNCTION
+ " (struct "
+ COMPILE_I_SIMPLE_REGISTER_STRUCT_TAG
+ " *"
+ COMPILE_I_SIMPLE_REGISTER_ARG_NAME
+ ") {\n",
+ buf);
+ break;
case COMPILE_I_RAW_SCOPE:
break;
default:
@@ -226,6 +236,7 @@ add_code_footer (enum compile_i_scope_types type, struct ui_file *buf)
case COMPILE_I_SIMPLE_SCOPE:
case COMPILE_I_PRINT_ADDRESS_SCOPE:
case COMPILE_I_PRINT_VALUE_SCOPE:
+ case COMPILE_I_PRINTF_SCOPE:
fputs_unfiltered ("}\n", buf);
break;
case COMPILE_I_RAW_SCOPE:
@@ -390,7 +401,8 @@ c_compute_program (struct compile_instance *inst,

if (inst->scope == COMPILE_I_SIMPLE_SCOPE
|| inst->scope == COMPILE_I_PRINT_ADDRESS_SCOPE
- || inst->scope == COMPILE_I_PRINT_VALUE_SCOPE)
+ || inst->scope == COMPILE_I_PRINT_VALUE_SCOPE
+ || inst->scope == COMPILE_I_PRINTF_SCOPE)
{
ui_file_put (var_stream, ui_file_write_for_put, buf);
fputs_unfiltered ("#pragma GCC user_expression\n", buf);
@@ -418,6 +430,11 @@ c_compute_program (struct compile_instance *inst,
(inst->scope == COMPILE_I_PRINT_ADDRESS_SCOPE
? "&" : ""));
break;
+ case COMPILE_I_PRINTF_SCOPE:
+ fprintf_unfiltered (buf,
+"printf (%s);\n"
+ , input);
+ break;
default:
fputs_unfiltered (input, buf);
break;
diff --git a/gdb/compile/compile-object-load.c b/gdb/compile/compile-object-load.c
index e67e5ba..bc74590 100644
--- a/gdb/compile/compile-object-load.c
+++ b/gdb/compile/compile-object-load.c
@@ -470,7 +470,7 @@ static struct type *
get_regs_type (struct symbol *func_sym, struct objfile *objfile)
{
struct type *func_type = SYMBOL_TYPE (func_sym);
- struct type *regsp_type, *regs_type;
+ struct type *regsp_type, *regs_type, *void_type, *voidp_type;

/* No register parameter present. */
if (TYPE_NFIELDS (func_type) == 0)
@@ -490,6 +490,18 @@ get_regs_type (struct symbol *func_sym, struct objfile *objfile)
TYPE_CODE (regs_type), GCC_FE_WRAPPER_FUNCTION,
objfile_name (objfile));

+ /* No register parameter present. */
+ if (TYPE_NFIELDS (func_type) == 1)
+ return regs_type;
+
+ void_type = builtin_type (target_gdbarch ())->builtin_void;
+ voidp_type = lookup_pointer_type (void_type);
+ if (!types_deeply_equal (voidp_type, TYPE_FIELD_TYPE (func_type, 1)))
+ error (_("Invalid type of function \"%s\" parameter \"%s\" in compiled "
+ "module \"%s\"."),
+ GCC_FE_WRAPPER_FUNCTION, COMPILE_I_PRINT_OUT_ARG,
+ objfile_name (objfile));
+
return regs_type;
}

@@ -637,6 +649,10 @@ compile_object_load (const char *object_file, const char *source_file,
expect_parameters = 2;
expect_return_type = builtin_type (target_gdbarch ())->builtin_void;
break;
+ case COMPILE_I_PRINTF_SCOPE:
+ expect_parameters = 1;
+ expect_return_type = builtin_type (target_gdbarch ())->builtin_void;
+ break;
default:
internal_error (__FILE__, __LINE__, _("invalid scope %d"), scope);
}
diff --git a/gdb/compile/compile.c b/gdb/compile/compile.c
index c4b8f2e..6bbe8df 100644
--- a/gdb/compile/compile.c
+++ b/gdb/compile/compile.c
@@ -174,17 +174,14 @@ compile_print_value (struct value *val, void *data_voidp)
print_value (val, fmtp);
}

-/* Handle the input from the 'compile print' command. The "compile
- print" command is used to evaluate and print an expression that may
- contain calls to the GCC compiler. The language expected in this
- compile command is the language currently set in GDB. */
+/* Implement 'compile print' and 'compile printf' command according
+ to SCOPE. */

static void
-compile_print_command (char *arg_param, int from_tty)
+compile_print (char *arg_param, enum compile_i_scope_types scope)
{
const char *arg = arg_param;
struct cleanup *cleanup;
- enum compile_i_scope_types scope = COMPILE_I_PRINT_ADDRESS_SCOPE;
struct format_data *fmtp;

cleanup = make_cleanup_restore_integer (&interpreter_async);
@@ -207,6 +204,25 @@ compile_print_command (char *arg_param, int from_tty)
do_cleanups (cleanup);
}

+/* Handle the input from the 'compile print' command. The "compile
+ print" command is used to evaluate and print an expression that may
+ contain calls to the GCC compiler. The language expected in this
+ compile command is the language currently set in GDB. */
+
+static void
+compile_print_command (char *arg, int from_tty)
+{
+ compile_print (arg, COMPILE_I_PRINT_ADDRESS_SCOPE);
+}
+
+/* Handle the input from the 'compile printf' command. */
+
+static void
+compile_printf_command (char *arg, int from_tty)
+{
+ compile_print (arg, COMPILE_I_PRINTF_SCOPE);
+}
+
/* A cleanup function to remove a directory and all its contents. */

static void
@@ -308,6 +324,14 @@ static char *compile_args;
static int compile_args_argc;
static char **compile_args_argv;

+/* String for 'set compile-printf-args' and 'show compile-printf-args'. */
+static char *compile_printf_args;
+
+/* Parsed form of COMPILE_PRINTF_ARGS.
+ COMPILE_PRINTF_ARGS_ARGV is NULL terminated. */
+static int compile_printf_args_argc;
+static char **compile_printf_args_argv;
+
/* Implement 'set compile-args'. */

static void
@@ -328,6 +352,27 @@ show_compile_args (struct ui_file *file, int from_tty,
value);
}

+/* Implement 'set compile-printf-args'. */
+
+static void
+set_compile_printf_args (char *args, int from_tty, struct cmd_list_element *c)
+{
+ freeargv (compile_printf_args_argv);
+ build_argc_argv (compile_printf_args,
+ &compile_printf_args_argc, &compile_printf_args_argv);
+}
+
+/* Implement 'show compile-printf-args'. */
+
+static void
+show_compile_printf_args (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Compile printf command command-line arguments "
+ "are \"%s\".\n"),
+ value);
+}
+
/* Append ARGC and ARGV (as parsed by build_argc_argv) to *ARGCP and *ARGVP.
ARGCP+ARGVP can be zero+NULL and also ARGC+ARGV can be zero+NULL. */

@@ -422,6 +467,10 @@ get_args (const struct compile_instance *compiler, struct gdbarch *gdbarch,
freeargv (argv_compiler);

append_args (argcp, argvp, compile_args_argc, compile_args_argv);
+
+ if (compiler->scope == COMPILE_I_PRINTF_SCOPE)
+ append_args (argcp, argvp,
+ compile_printf_args_argc, compile_printf_args_argv);
}

/* A cleanup function to destroy a gdb_gcc_instance. */
@@ -727,6 +776,24 @@ EXPR may be preceded with /FMT, where FMT is a format letter\n\
but no count or size letter (see \"x\" command)."),
&compile_command_list);

+ add_cmd ("printf", class_obscure, compile_printf_command,
+ _("\
+Print formatted output with the compiler.\n\
+\n\
+Usage: compile printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
+\n\
+The arguments may be specified on the same line as the command, e.g.:\n\
+\n\
+ compile printf \"%d\\n\", i\n\
+\n\
+Alternatively, you can type the arguments interactively.\n\
+You can invoke this mode when no argument is given to the command\n\
+(i.e., \"compile printf\" is typed with nothing after it). An\n\
+interactive prompt will be shown allowing you to enter multiple\n\
+lines of source code. Type a line containing \"end\" to indicate\n\
+the end of the source code."),
+ &compile_command_list);
+
add_setshow_boolean_cmd ("compile", class_maintenance, &compile_debug, _("\
Set compile command debugging."), _("\
Show compile command debugging."), _("\
@@ -744,6 +811,15 @@ String quoting is parsed like in shell, for example:\n\
-mno-align-double \"-I/dir with a space/include\""),
set_compile_args, show_compile_args, &setlist, &showlist);

+ add_setshow_string_cmd ("compile-printf-args", class_support,
+ &compile_printf_args,
+ _("Set compile command GCC command-line arguments FIXME"),
+ _("Show compile command GCC command-line arguments FIXME"),
+ _("\
+Use options like -Werror which get added for 'compile printf' command."),
+ set_compile_printf_args, show_compile_printf_args,
+ &setlist, &showlist);
+
/* Override flags possibly coming from DW_AT_producer. */
compile_args = xstrdup ("-O0 -gdwarf-4"
/* We use -fPIE Otherwise GDB would need to reserve space large enough for
@@ -762,4 +838,7 @@ String quoting is parsed like in shell, for example:\n\
" -fno-stack-protector"
);
set_compile_args (compile_args, 0, NULL);
+
+ compile_printf_args = xstrdup ("-Werror=format");
+ set_compile_printf_args (compile_printf_args, 0, NULL);
}
diff --git a/gdb/defs.h b/gdb/defs.h
index f6e010c..0bcb7f0 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -79,6 +79,10 @@ enum compile_i_scope_types
name already specifies its address. */
COMPILE_I_PRINT_ADDRESS_SCOPE,
COMPILE_I_PRINT_VALUE_SCOPE,
+
+ /* Implement the "compile printf" command. SCOPE_DATA contains
+ string containing the format string together with its arguments. */
+ COMPILE_I_PRINTF_SCOPE,
};

/* Just in case they're not defined in stdio.h. */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3eaf9d3..46a7bf4 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -17242,6 +17242,20 @@ command without any text following the command. This will start the
multiple-line editor.
@end table

+@table @code
+@item compile printf @var{template}, @var{expressions}@dots{}
+Compile and execute @code{printf} function call with the compiler
+language found as the current language in @value{GDBN}
+(@pxref{Languages}).
+
+@item compile printf
+@cindex reprint the last value
+Alternatively you can enter the template and expression (source code
+producing them) as multiple lines of text. To enter this mode, invoke
+the @samp{compile printf} command without any text following the
+command. This will start the multiple-line editor.
+@end table
+
@subsection Caveats when using the @code{compile} command

There are a few caveats to keep in mind when using the @code{compile}
diff --git a/gdb/testsuite/gdb.compile/compile-print.exp b/gdb/testsuite/gdb.compile/compile-print.exp
index 92c6240..14fd489 100644
--- a/gdb/testsuite/gdb.compile/compile-print.exp
+++ b/gdb/testsuite/gdb.compile/compile-print.exp
@@ -56,3 +56,8 @@ gdb_test "compile print/x 256" " = 0x100"
gdb_test {print $} " = 256"

gdb_test "compile print varobject" { = {field = 1}}
+
+if ![is_remote target] {
+ gdb_test {compile printf "0x%x\n", varint} "\r\n0xa"
+ gdb_test {compile printf "0x%x\n"} "\r\nCompilation failed\\."
+}