aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorStan Shebs <shebs@codesourcery.com>2012-07-02 15:29:39 +0000
committerStan Shebs <shebs@codesourcery.com>2012-07-02 15:29:39 +0000
commitd3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2 (patch)
tree782811ca1df7a4f775823d1918d0b571fdac9b5a /gdb
parenta47edf2745dd6414d635e4b372d416035c7b8c12 (diff)
downloadgdb-d3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2.zip
gdb-d3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2.tar.gz
gdb-d3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2.tar.bz2
Add target-side support for dynamic printf.
* NEWS: Mention the additional style. * breakpoint.h (struct bp_target_info): New fields tcommands, persist. (struct bp_location): New field cmd_bytecode. * breakpoint.c: Include format.h. (disconnected_dprintf): New global. (parse_cmd_to_aexpr): New function. (build_target_command_list): New function. (insert_bp_location): Call it. (remove_breakpoints_pid): Skip dprintf breakpoints. (print_one_breakpoint_location): Ditto. (dprintf_style_agent): New global. (dprintf_style_enums): Add dprintf_style_agent. (update_dprintf_command_list): Add agent case. (agent_printf_command): New function. (_initialize_breakpoint): Add new commands. * common/ax.def (printf): New bytecode. * ax.h (ax_string): Declare. * ax-gdb.h (gen_printf): Declare. * ax-gdb.c: Include cli-utils.h, format.h. (gen_printf): New function. (maint_agent_print_command): New function. (_initialize_ax_gdb): Add maint agent-printf command. * ax-general.c (ax_string): New function. (ax_print): Add printf disassembly. * Makefile.in (SFILES): Add format.c (COMMON_OBS): Add format.o. * common/format.h: New file. * common/format.c: New file. * printcmd.c: Include format.h. (ui_printf): Call parse_format_string. * remote.c (remote_state): New field breakpoint_commands. (PACKET_BreakpointCommands): New enum. (remote_breakpoint_commands_feature): New function. (remote_protocol_features): Add new BreakpointCommands entry. (remote_can_run_breakpoint_commands): New function. (remote_add_target_side_commands): New function. (remote_insert_breakpoint): Call it. (remote_insert_hw_breakpoint): Ditto. (_initialize_remote): Add new packet configuration for target-side breakpoint commands. * target.h (struct target_ops): New field to_can_run_breakpoint_commands. (target_can_run_breakpoint_commands): New macro. * target.c (update_current_target): Handle to_can_run_breakpoint_commands. [gdbserver] * Makefile.in (WARN_CFLAGS_NO_FORMAT): Define. (ax.o): Add it to build rule. (ax-ipa.o): Ditto. (OBS): Add format.o. (IPA_OBS): Add format.o. * server.c (handle_query): Claim support for breakpoint commands. (process_point_options): Add command case. (process_serial_event): Leave running if there are printfs in effect. * mem-break.h (any_persistent_commands): Declare. (add_breakpoint_commands): Declare. (gdb_no_commands_at_breakpoint): Declare. (run_breakpoint_commands): Declare. * mem-break.c (struct point_command_list): New struct. (struct breakpoint): New field command_list. (any_persistent_commands): New function. (add_commands_to_breakpoint): New function. (add_breakpoint_commands): New function. (gdb_no_commands_at_breakpoint): New function. (run_breakpoint_commands): New function. * linux-low.c (linux_wait_1): Test for and run breakpoint commands locally. * ax.c: Include format.h. (ax_printf): New function. (gdb_eval_agent_expr): Add printf opcode. [doc] * gdb.texinfo (Dynamic Printf): Mention agent style and disconnected dprintf. (Maintenance Commands): Describe maint agent-printf. (General Query Packets): Mention BreakpointCommands feature. (Packets): Document commands extension to Z0 packet. * agentexpr.texi (Bytecode Descriptions): Document printf bytecode. [testsuite] * gdb.base/dprintf.exp: Add agent style tests.
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog49
-rw-r--r--gdb/Makefile.in11
-rw-r--r--gdb/NEWS14
-rw-r--r--gdb/ax-gdb.c143
-rw-r--r--gdb/ax-gdb.h6
-rw-r--r--gdb/ax-general.c37
-rw-r--r--gdb/ax.h3
-rw-r--r--gdb/breakpoint.c329
-rw-r--r--gdb/breakpoint.h15
-rw-r--r--gdb/common/ax.def2
-rw-r--r--gdb/common/format.c400
-rw-r--r--gdb/common/format.h63
-rw-r--r--gdb/doc/ChangeLog10
-rw-r--r--gdb/doc/agentexpr.texi17
-rw-r--r--gdb/doc/gdb.texinfo60
-rw-r--r--gdb/gdbserver/ChangeLog28
-rw-r--r--gdb/gdbserver/Makefile.in15
-rw-r--r--gdb/gdbserver/ax.c155
-rw-r--r--gdb/gdbserver/linux-low.c8
-rw-r--r--gdb/gdbserver/mem-break.c125
-rw-r--r--gdb/gdbserver/mem-break.h8
-rw-r--r--gdb/gdbserver/server.c53
-rw-r--r--gdb/printcmd.c341
-rw-r--r--gdb/remote.c69
-rw-r--r--gdb/target.c4
-rw-r--r--gdb/target.h10
-rw-r--r--gdb/testsuite/ChangeLog4
-rw-r--r--gdb/testsuite/gdb.base/dprintf.exp24
28 files changed, 1619 insertions, 384 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 2a910ba..281099b 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,52 @@
+2012-07-02 Stan Shebs <stan@codesourcery.com>
+
+ Add target-side support for dynamic printf.
+ * NEWS: Mention the additional style.
+ * breakpoint.h (struct bp_target_info): New fields tcommands, persist.
+ (struct bp_location): New field cmd_bytecode.
+ * breakpoint.c: Include format.h.
+ (disconnected_dprintf): New global.
+ (parse_cmd_to_aexpr): New function.
+ (build_target_command_list): New function.
+ (insert_bp_location): Call it.
+ (remove_breakpoints_pid): Skip dprintf breakpoints.
+ (print_one_breakpoint_location): Ditto.
+ (dprintf_style_agent): New global.
+ (dprintf_style_enums): Add dprintf_style_agent.
+ (update_dprintf_command_list): Add agent case.
+ (agent_printf_command): New function.
+ (_initialize_breakpoint): Add new commands.
+ * common/ax.def (printf): New bytecode.
+ * ax.h (ax_string): Declare.
+ * ax-gdb.h (gen_printf): Declare.
+ * ax-gdb.c: Include cli-utils.h, format.h.
+ (gen_printf): New function.
+ (maint_agent_print_command): New function.
+ (_initialize_ax_gdb): Add maint agent-printf command.
+ * ax-general.c (ax_string): New function.
+ (ax_print): Add printf disassembly.
+ * Makefile.in (SFILES): Add format.c
+ (COMMON_OBS): Add format.o.
+ * common/format.h: New file.
+ * common/format.c: New file.
+ * printcmd.c: Include format.h.
+ (ui_printf): Call parse_format_string.
+ * remote.c (remote_state): New field breakpoint_commands.
+ (PACKET_BreakpointCommands): New enum.
+ (remote_breakpoint_commands_feature): New function.
+ (remote_protocol_features): Add new BreakpointCommands entry.
+ (remote_can_run_breakpoint_commands): New function.
+ (remote_add_target_side_commands): New function.
+ (remote_insert_breakpoint): Call it.
+ (remote_insert_hw_breakpoint): Ditto.
+ (_initialize_remote): Add new packet configuration for
+ target-side breakpoint commands.
+ * target.h (struct target_ops): New field
+ to_can_run_breakpoint_commands.
+ (target_can_run_breakpoint_commands): New macro.
+ * target.c (update_current_target): Handle
+ to_can_run_breakpoint_commands.
+
2012-07-02 Jan Kratochvil <jan.kratochvil@redhat.com>
Execute -ix and -iex only after system and user gdbinit files.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bf6b0da..a41cff9 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -745,7 +745,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \
regset.c sol-thread.c windows-termcap.c \
common/common-utils.c common/xml-utils.c \
- common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c
+ common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \
+ common/format.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -827,6 +828,7 @@ gnulib/import/extra/snippet/arg-nonnull.h gnulib/import/extra/snippet/c++defs.h
gnulib/import/extra/snippet/warn-on-use.h \
gnulib/import/stddef.in.h gnulib/import/inttypes.in.h inline-frame.h skip.h \
common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \
+common/format.h \
common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -916,7 +918,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
inferior.o osdata.o gdb_usleep.o record.o gcore.o \
jit.o progspace.o skip.o probe.o \
- common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o
+ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
+ format.o
TSOBS = inflow.o
@@ -1916,6 +1919,10 @@ buffer.o: ${srcdir}/common/buffer.c
$(COMPILE) $(srcdir)/common/buffer.c
$(POSTCOMPILE)
+format.o: ${srcdir}/common/format.c
+ $(COMPILE) $(srcdir)/common/format.c
+ $(POSTCOMPILE)
+
linux-osdata.o: ${srcdir}/common/linux-osdata.c
$(COMPILE) $(srcdir)/common/linux-osdata.c
$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index 5cfb48a..a1e3dc8 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -244,11 +244,12 @@ set debug auto-load on|off
show debug auto-load
Control display of debugging info for auto-loading the files above.
-set dprintf-style gdb|call
+set dprintf-style gdb|call|agent
show dprintf-style
- Control the way in which a dynamic printf is performed; "gdb" requests
- a GDB printf command, while "call" causes dprintf to call a function
- in the inferior.
+ Control the way in which a dynamic printf is performed; "gdb"
+ requests a GDB printf command, while "call" causes dprintf to call a
+ function in the inferior. "agent" requests that the target agent
+ (such as GDBserver) do the printing.
set dprintf-function <expr>
show dprintf-function
@@ -257,6 +258,11 @@ show dprintf-channel
Set the function and optional first argument to the call when using
the "call" style of dynamic printf.
+set disconnected-dprintf on|off
+show disconnected-dprintf
+ Control whether agent-style dynamic printfs continue to be in effect
+ after GDB disconnects.
+
* New configure options
--with-auto-load-dir
diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c
index 1bf03df..031ccfc 100644
--- a/gdb/ax-gdb.c
+++ b/gdb/ax-gdb.c
@@ -41,10 +41,13 @@
#include "tracepoint.h"
#include "cp-support.h"
#include "arch-utils.h"
+#include "cli/cli-utils.h"
#include "valprint.h"
#include "c-lang.h"
+#include "format.h"
+
/* To make sense of this file, you should read doc/agentexpr.texi.
Then look at the types and enums in ax-gdb.h. For the code itself,
look at gen_expr, towards the bottom; that's the main function that
@@ -2503,6 +2506,59 @@ gen_trace_for_return_address (CORE_ADDR scope, struct gdbarch *gdbarch)
return ax;
}
+/* Given a collection of printf-style arguments, generate code to
+ evaluate the arguments and pass everything to a special
+ bytecode. */
+
+struct agent_expr *
+gen_printf (CORE_ADDR scope, struct gdbarch *gdbarch,
+ CORE_ADDR function, LONGEST channel,
+ char *format, int fmtlen,
+ struct format_piece *frags,
+ int nargs, struct expression **exprs)
+{
+ struct expression *expr;
+ struct cleanup *old_chain = 0;
+ struct agent_expr *ax = new_agent_expr (gdbarch, scope);
+ union exp_element *pc;
+ struct axs_value value;
+ int i, tem, bot, fr, flen;
+ char *fmt;
+
+ old_chain = make_cleanup_free_agent_expr (ax);
+
+ /* Evaluate and push the args on the stack in reverse order,
+ for simplicity of collecting them on the target side. */
+ for (tem = nargs - 1; tem >= 0; --tem)
+ {
+ pc = exprs[tem]->elts;
+ /* We're computing values, not doing side effects. */
+ trace_kludge = 0;
+ value.optimized_out = 0;
+ gen_expr (exprs[tem], &pc, ax, &value);
+ require_rvalue (ax, &value);
+ }
+
+ /* Push function and channel. */
+ ax_const_l (ax, channel);
+ ax_const_l (ax, function);
+
+ /* Issue the printf bytecode proper. */
+ ax_simple (ax, aop_printf);
+ ax_simple (ax, nargs);
+ ax_string (ax, format, fmtlen);
+
+ /* And terminate. */
+ ax_simple (ax, aop_end);
+
+ /* We have successfully built the agent expr, so cancel the cleanup
+ request. If we add more cleanups that we always want done, this
+ will have to get more complicated. */
+ discard_cleanups (old_chain);
+
+ return ax;
+}
+
static void
agent_command (char *exp, int from_tty)
{
@@ -2586,6 +2642,88 @@ agent_eval_command (char *exp, int from_tty)
do_cleanups (old_chain);
dont_repeat ();
}
+/* Parse the given expression, compile it into an agent expression
+ that does a printf, and display the resulting expression. */
+
+static void
+maint_agent_printf_command (char *exp, int from_tty)
+{
+ struct cleanup *old_chain = 0;
+ struct expression *expr;
+ struct expression *argvec[100];
+ struct agent_expr *agent;
+ struct frame_info *fi = get_current_frame (); /* need current scope */
+ char *cmdrest;
+ char *format_start, *format_end;
+ struct format_piece *fpieces;
+ int nargs;
+
+ /* We don't deal with overlay debugging at the moment. We need to
+ think more carefully about this. If you copy this code into
+ another command, change the error message; the user shouldn't
+ have to know anything about agent expressions. */
+ if (overlay_debugging)
+ error (_("GDB can't do agent expression translation with overlays."));
+
+ if (exp == 0)
+ error_no_arg (_("expression to translate"));
+
+ cmdrest = exp;
+
+ cmdrest = skip_spaces (cmdrest);
+
+ if (*cmdrest++ != '"')
+ error (_("Must start with a format string."));
+
+ format_start = cmdrest;
+
+ fpieces = parse_format_string (&cmdrest);
+
+ old_chain = make_cleanup (free_format_pieces_cleanup, &fpieces);
+
+ format_end = cmdrest;
+
+ if (*cmdrest++ != '"')
+ error (_("Bad format string, non-terminated '\"'."));
+
+ cmdrest = skip_spaces (cmdrest);
+
+ if (*cmdrest != ',' && *cmdrest != 0)
+ error (_("Invalid argument syntax"));
+
+ if (*cmdrest == ',')
+ cmdrest++;
+ cmdrest = skip_spaces (cmdrest);
+
+ nargs = 0;
+ while (*cmdrest != '\0')
+ {
+ char *cmd1;
+
+ cmd1 = cmdrest;
+ expr = parse_exp_1 (&cmd1, 0, (struct block *) 0, 1);
+ argvec[nargs] = expr;
+ ++nargs;
+ cmdrest = cmd1;
+ if (*cmdrest == ',')
+ ++cmdrest;
+ /* else complain? */
+ }
+
+
+ agent = gen_printf (get_frame_pc (fi), get_current_arch (), 0, 0,
+ format_start, format_end - format_start,
+ fpieces, nargs, argvec);
+ make_cleanup_free_agent_expr (agent);
+ ax_reqs (agent);
+ ax_print (gdb_stdout, agent);
+
+ /* It would be nice to call ax_reqs here to gather some general info
+ about the expression, and then print out the result. */
+
+ do_cleanups (old_chain);
+ dont_repeat ();
+}
/* Initialization code. */
@@ -2603,4 +2741,9 @@ _initialize_ax_gdb (void)
_("Translate an expression into remote "
"agent bytecode for evaluation."),
&maintenancelist);
+
+ add_cmd ("agent-printf", class_maintenance, maint_agent_printf_command,
+ _("Translate an expression into remote "
+ "agent bytecode for evaluation and display the bytecodes."),
+ &maintenancelist);
}
diff --git a/gdb/ax-gdb.h b/gdb/ax-gdb.h
index 09f6889..c357fb6 100644
--- a/gdb/ax-gdb.h
+++ b/gdb/ax-gdb.h
@@ -115,6 +115,12 @@ extern void gen_expr (struct expression *exp, union exp_element **pc,
extern void require_rvalue (struct agent_expr *ax, struct axs_value *value);
+struct format_piece;
+extern struct agent_expr *gen_printf (CORE_ADDR, struct gdbarch *,
+ CORE_ADDR, LONGEST, char *, int,
+ struct format_piece *,
+ int, struct expression **);
+
extern int trace_kludge;
extern int trace_string_kludge;
diff --git a/gdb/ax-general.c b/gdb/ax-general.c
index 4f1ea20..6ea6f14 100644
--- a/gdb/ax-general.c
+++ b/gdb/ax-general.c
@@ -330,6 +330,30 @@ ax_tsv (struct agent_expr *x, enum agent_op op, int num)
x->buf[x->len + 2] = (num) & 0xff;
x->len += 3;
}
+
+/* Append a string to the expression. Note that the string is going
+ into the bytecodes directly, not on the stack. As a precaution,
+ include both length as prefix, and terminate with a NUL. (The NUL
+ is counted in the length.) */
+
+void
+ax_string (struct agent_expr *x, char *str, int slen)
+{
+ int i;
+
+ /* Make sure the string length is reasonable. */
+ if (slen < 0 || slen > 0xffff)
+ internal_error (__FILE__, __LINE__,
+ _("ax-general.c (ax_string): string "
+ "length is %d, out of allowed range"), slen);
+
+ grow_expr (x, 2 + slen + 1);
+ x->buf[x->len++] = ((slen + 1) >> 8) & 0xff;
+ x->buf[x->len++] = (slen + 1) & 0xff;
+ for (i = 0; i < slen; ++i)
+ x->buf[x->len++] = str[i];
+ x->buf[x->len++] = '\0';
+}
@@ -391,6 +415,19 @@ ax_print (struct ui_file *f, struct agent_expr *x)
print_longest (f, 'd', 0,
read_const (x, i + 1, aop_map[op].op_size));
}
+ /* Handle the complicated printf arguments specially. */
+ else if (op == aop_printf)
+ {
+ int slen, nargs;
+
+ i++;
+ nargs = x->buf[i++];
+ slen = x->buf[i++];
+ slen = slen * 256 + x->buf[i++];
+ fprintf_filtered (f, _(" \"%s\", %d args"),
+ &(x->buf[i]), nargs);
+ i += slen - 1;
+ }
fprintf_filtered (f, "\n");
i += 1 + aop_map[op].op_size;
diff --git a/gdb/ax.h b/gdb/ax.h
index d4069f7..368f727 100644
--- a/gdb/ax.h
+++ b/gdb/ax.h
@@ -219,6 +219,9 @@ extern void ax_reg_mask (struct agent_expr *ax, int reg);
/* Assemble code to operate on a trace state variable. */
extern void ax_tsv (struct agent_expr *expr, enum agent_op op, int num);
+
+/* Append a string to the bytecode stream. */
+extern void ax_string (struct agent_expr *x, char *str, int slen);
/* Functions for printing out expressions, and otherwise debugging
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 06e57b3..555694e 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -70,6 +70,8 @@
#include "ax-gdb.h"
#include "dummy-frame.h"
+#include "format.h"
+
/* readline include files */
#include "readline/readline.h"
#include "readline/history.h"
@@ -301,6 +303,45 @@ static struct breakpoint_ops bkpt_probe_breakpoint_ops;
/* Dynamic printf class type. */
static struct breakpoint_ops dprintf_breakpoint_ops;
+/* The style in which to perform a dynamic printf. This is a user
+ option because different output options have different tradeoffs;
+ if GDB does the printing, there is better error handling if there
+ is a problem with any of the arguments, but using an inferior
+ function lets you have special-purpose printers and sending of
+ output to the same place as compiled-in print functions. */
+
+static const char dprintf_style_gdb[] = "gdb";
+static const char dprintf_style_call[] = "call";
+static const char dprintf_style_agent[] = "agent";
+static const char *const dprintf_style_enums[] = {
+ dprintf_style_gdb,
+ dprintf_style_call,
+ dprintf_style_agent,
+ NULL
+};
+static const char *dprintf_style = dprintf_style_gdb;
+
+/* The function to use for dynamic printf if the preferred style is to
+ call into the inferior. The value is simply a string that is
+ copied into the command, so it can be anything that GDB can
+ evaluate to a callable address, not necessarily a function name. */
+
+static char *dprintf_function = "";
+
+/* The channel to use for dynamic printf if the preferred style is to
+ call into the inferior; if a nonempty string, it will be passed to
+ the call as the first argument, with the format string as the
+ second. As with the dprintf function, this can be anything that
+ GDB knows how to evaluate, so in addition to common choices like
+ "stderr", this could be an app-specific expression like
+ "mystreams[curlogger]". */
+
+static char *dprintf_channel = "";
+
+/* True if dprintf commands should continue to operate even if GDB
+ has disconnected. */
+static int disconnected_dprintf = 1;
+
/* A reference-counted struct command_line. This lets multiple
breakpoints share a single command list. */
struct counted_command_line
@@ -2132,6 +2173,196 @@ build_target_condition_list (struct bp_location *bl)
return;
}
+/* Parses a command described by string CMD into an agent expression
+ bytecode suitable for evaluation by the bytecode interpreter.
+ Return NULL if there was any error during parsing. */
+
+static struct agent_expr *
+parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd)
+{
+ struct cleanup *old_cleanups = 0;
+ struct expression *expr, **argvec;
+ struct agent_expr *aexpr = NULL;
+ struct cleanup *old_chain = NULL;
+ volatile struct gdb_exception ex;
+ char *cmdrest;
+ char *format_start, *format_end;
+ struct format_piece *fpieces;
+ int nargs;
+ struct gdbarch *gdbarch = get_current_arch ();
+
+ if (!cmd)
+ return NULL;
+
+ cmdrest = cmd;
+
+ if (*cmdrest == ',')
+ ++cmdrest;
+ cmdrest = skip_spaces (cmdrest);
+
+ if (*cmdrest++ != '"')
+ error (_("No format string following the location"));
+
+ format_start = cmdrest;
+
+ fpieces = parse_format_string (&cmdrest);
+
+ old_cleanups = make_cleanup (free_format_pieces_cleanup, &fpieces);
+
+ format_end = cmdrest;
+
+ if (*cmdrest++ != '"')
+ error (_("Bad format string, non-terminated '\"'."));
+
+ cmdrest = skip_spaces (cmdrest);
+
+ if (!(*cmdrest == ',' || *cmdrest == '\0'))
+ error (_("Invalid argument syntax"));
+
+ if (*cmdrest == ',')
+ cmdrest++;
+ cmdrest = skip_spaces (cmdrest);
+
+ /* For each argument, make an expression. */
+
+ argvec = (struct expression **) alloca (strlen (cmd)
+ * sizeof (struct expression *));
+
+ nargs = 0;
+ while (*cmdrest != '\0')
+ {
+ char *cmd1;
+
+ cmd1 = cmdrest;
+ expr = parse_exp_1 (&cmd1, scope, block_for_pc (scope), 1);
+ argvec[nargs++] = expr;
+ cmdrest = cmd1;
+ if (*cmdrest == ',')
+ ++cmdrest;
+ }
+
+ /* We don't want to stop processing, so catch any errors
+ that may show up. */
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ aexpr = gen_printf (scope, gdbarch, 0, 0,
+ format_start, format_end - format_start,
+ fpieces, nargs, argvec);
+ }
+
+ if (ex.reason < 0)
+ {
+ /* If we got here, it means the command could not be parsed to a valid
+ bytecode expression and thus can't be evaluated on the target's side.
+ It's no use iterating through the other commands. */
+ return NULL;
+ }
+
+ do_cleanups (old_cleanups);
+
+ /* We have a valid agent expression, return it. */
+ return aexpr;
+}
+
+/* Based on location BL, create a list of breakpoint commands to be
+ passed on to the target. If we have duplicated locations with
+ different commands, we will add any such to the list. */
+
+static void
+build_target_command_list (struct bp_location *bl)
+{
+ struct bp_location **locp = NULL, **loc2p;
+ int null_command_or_parse_error = 0;
+ int modified = bl->needs_update;
+ struct bp_location *loc;
+
+ /* For now, limit to agent-style dprintf breakpoints. */
+ if (bl->owner->type != bp_dprintf
+ || strcmp (dprintf_style, dprintf_style_agent) != 0)
+ return;
+
+ if (!target_can_run_breakpoint_commands ())
+ return;
+
+ /* Do a first pass to check for locations with no assigned
+ conditions or conditions that fail to parse to a valid agent expression
+ bytecode. If any of these happen, then it's no use to send conditions
+ to the target since this location will always trigger and generate a
+ response back to GDB. */
+ ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
+ {
+ loc = (*loc2p);
+ if (is_breakpoint (loc->owner) && loc->pspace->num == bl->pspace->num)
+ {
+ if (modified)
+ {
+ struct agent_expr *aexpr;
+
+ /* Re-parse the commands since something changed. In that
+ case we already freed the command bytecodes (see
+ force_breakpoint_reinsertion). We just
+ need to parse the command to bytecodes again. */
+ aexpr = parse_cmd_to_aexpr (bl->address,
+ loc->owner->extra_string);
+ loc->cmd_bytecode = aexpr;
+
+ if (!aexpr)
+ continue;
+ }
+
+ /* If we have a NULL bytecode expression, it means something
+ went wrong or we have a null command expression. */
+ if (!loc->cmd_bytecode)
+ {
+ null_command_or_parse_error = 1;
+ break;
+ }
+ }
+ }
+
+ /* If anything failed, then we're not doing target-side commands,
+ and so clean up. */
+ if (null_command_or_parse_error)
+ {
+ ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
+ {
+ loc = (*loc2p);
+ if (is_breakpoint (loc->owner)
+ && loc->pspace->num == bl->pspace->num)
+ {
+ /* Only go as far as the first NULL bytecode is
+ located. */
+ if (!loc->cond_bytecode)
+ return;
+
+ free_agent_expr (loc->cond_bytecode);
+ loc->cond_bytecode = NULL;
+ }
+ }
+ }
+
+ /* No NULL commands or failed bytecode generation. Build a command list
+ for this location's address. */
+ ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
+ {
+ loc = (*loc2p);
+ if (loc->owner->extra_string
+ && is_breakpoint (loc->owner)
+ && loc->pspace->num == bl->pspace->num
+ && loc->owner->enable_state == bp_enabled
+ && loc->enabled)
+ /* Add the command to the vector. This will be used later
+ to send the commands to the target. */
+ VEC_safe_push (agent_expr_p, bl->target_info.tcommands,
+ loc->cmd_bytecode);
+ }
+
+ bl->target_info.persist = 0;
+ /* Maybe flag this location as persistent. */
+ if (bl->owner->type == bp_dprintf && disconnected_dprintf)
+ bl->target_info.persist = 1;
+}
+
/* Insert a low-level "breakpoint" of some type. BL is the breakpoint
location. Any error messages are printed to TMP_ERROR_STREAM; and
DISABLED_BREAKS, and HW_BREAKPOINT_ERROR are used to report problems.
@@ -2172,7 +2403,8 @@ insert_bp_location (struct bp_location *bl,
if (is_breakpoint (bl->owner))
{
build_target_condition_list (bl);
- /* Reset the condition modification marker. */
+ build_target_command_list (bl);
+ /* Reset the modification marker. */
bl->needs_update = 0;
}
@@ -2691,6 +2923,9 @@ remove_breakpoints_pid (int pid)
if (bl->pspace != inf->pspace)
continue;
+ if (bl->owner->type == bp_dprintf)
+ continue;
+
if (bl->inserted)
{
val = remove_breakpoint (bl, mark_uninserted);
@@ -5827,6 +6062,15 @@ print_one_breakpoint_location (struct breakpoint *b,
}
}
+ if (!part_of_multiple && b->extra_string
+ && b->type == bp_dprintf && !b->commands)
+ {
+ annotate_field (7);
+ ui_out_text (uiout, "\t(agent printf) ");
+ ui_out_field_string (uiout, "printf", b->extra_string);
+ ui_out_text (uiout, "\n");
+ }
+
l = b->commands ? b->commands->commands : NULL;
if (!part_of_multiple && l)
{
@@ -8525,40 +8769,6 @@ bp_loc_is_permanent (struct bp_location *loc)
return retval;
}
-/* The style in which to perform a dynamic printf. This is a user
- option because different output options have different tradeoffs;
- if GDB does the printing, there is better error handling if there
- is a problem with any of the arguments, but using an inferior
- function lets you have special-purpose printers and sending of
- output to the same place as compiled-in print functions. (Future
- styles may include the ability to do a target-side printf.) */
-
-static const char dprintf_style_gdb[] = "gdb";
-static const char dprintf_style_call[] = "call";
-static const char *const dprintf_style_enums[] = {
- dprintf_style_gdb,
- dprintf_style_call,
- NULL
-};
-static const char *dprintf_style = dprintf_style_gdb;
-
-/* The function to use for dynamic printf if the preferred style is to
- call into the inferior. The value is simply a string that is
- copied into the command, so it can be anything that GDB can
- evaluate to a callable address, not necessarily a function name. */
-
-static char *dprintf_function = "";
-
-/* The channel to use for dynamic printf if the preferred style is to
- call into the inferior; if a nonempty string, it will be passed to
- the call as the first argument, with the format string as the
- second. As with the dprintf function, this can be anything that
- GDB knows how to evaluate, so in addition to common choices like
- "stderr", this could be an app-specific expression like
- "mystreams[curlogger]". */
-
-static char *dprintf_channel = "";
-
/* Build a command list for the dprintf corresponding to the current
settings of the dprintf style options. */
@@ -8582,9 +8792,9 @@ update_dprintf_command_list (struct breakpoint *b)
if (*dprintf_args != '"')
error (_("Bad format string, missing '\"'."));
- if (strcmp (dprintf_style, "gdb") == 0)
+ if (strcmp (dprintf_style, dprintf_style_gdb) == 0)
printf_line = xstrprintf ("printf %s", dprintf_args);
- else if (strcmp (dprintf_style, "call") == 0)
+ else if (strcmp (dprintf_style, dprintf_style_call) == 0)
{
if (!dprintf_function)
error (_("No function supplied for dprintf call"));
@@ -8599,6 +8809,16 @@ update_dprintf_command_list (struct breakpoint *b)
dprintf_function,
dprintf_args);
}
+ else if (strcmp (dprintf_style, dprintf_style_agent) == 0)
+ {
+ if (target_can_run_breakpoint_commands ())
+ printf_line = xstrprintf ("agent-printf %s", dprintf_args);
+ else
+ {
+ warning (_("Target cannot run dprintf commands, falling back to GDB printf"));
+ printf_line = xstrprintf ("printf %s", dprintf_args);
+ }
+ }
else
internal_error (__FILE__, __LINE__,
_("Invalid dprintf style."));
@@ -8608,12 +8828,15 @@ update_dprintf_command_list (struct breakpoint *b)
{
struct command_line *printf_cmd_line, *cont_cmd_line = NULL;
- cont_cmd_line = xmalloc (sizeof (struct command_line));
- cont_cmd_line->control_type = simple_control;
- cont_cmd_line->body_count = 0;
- cont_cmd_line->body_list = NULL;
- cont_cmd_line->next = NULL;
- cont_cmd_line->line = xstrdup ("continue");
+ if (strcmp (dprintf_style, dprintf_style_agent) != 0)
+ {
+ cont_cmd_line = xmalloc (sizeof (struct command_line));
+ cont_cmd_line->control_type = simple_control;
+ cont_cmd_line->body_count = 0;
+ cont_cmd_line->body_list = NULL;
+ cont_cmd_line->next = NULL;
+ cont_cmd_line->line = xstrdup ("continue");
+ }
printf_cmd_line = xmalloc (sizeof (struct command_line));
printf_cmd_line->control_type = simple_control;
@@ -9570,6 +9793,12 @@ dprintf_command (char *arg, int from_tty)
0);
}
+static void
+agent_printf_command (char *arg, int from_tty)
+{
+ error (_("May only run agent-printf on the target"));
+}
+
/* Implement the "breakpoint_hit" breakpoint_ops method for
ranged breakpoints. */
@@ -16244,6 +16473,20 @@ Show the channel to use for dynamic printf"), NULL,
update_dprintf_commands, NULL,
&setlist, &showlist);
+ add_setshow_boolean_cmd ("disconnected-dprintf", no_class,
+ &disconnected_dprintf, _("\
+Set whether dprintf continues after GDB disconnects."), _("\
+Show whether dprintf continues after GDB disconnects."), _("\
+Use this to let dprintf commands continue to hit and produce output\n\
+even if GDB disconnects or detaches from the target."),
+ NULL,
+ NULL,
+ &setlist, &showlist);
+
+ add_com ("agent-printf", class_vars, agent_printf_command, _("\
+agent-printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
+(target agent only) This is useful for formatted output in user-defined commands."));
+
automatic_hardware_breakpoints = 1;
observer_attach_about_to_proceed (breakpoint_about_to_proceed);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 68a9688..4e4f875 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -272,6 +272,14 @@ struct bp_target_info
/* Vector of conditions the target should evaluate if it supports target-side
breakpoint conditions. */
VEC(agent_expr_p) *conditions;
+
+ /* Vector of commands the target should evaluate if it supports
+ target-side breakpoint commands. */
+ VEC(agent_expr_p) *tcommands;
+
+ /* Flag that is true if the breakpoint should be left in place even
+ when GDB is not connected. */
+ int persist;
};
/* GDB maintains two types of information about each breakpoint (or
@@ -358,8 +366,11 @@ struct bp_location
enum condition_status condition_changed;
- /* Signals that breakpoint conditions need to be re-synched with the
- target. This has no use other than target-side breakpoints. */
+ struct agent_expr *cmd_bytecode;
+
+ /* Signals that breakpoint conditions and/or commands need to be
+ re-synched with the target. This has no use other than
+ target-side breakpoints. */
char needs_update;
/* This location's address is in an unloaded solib, and so this
diff --git a/gdb/common/ax.def b/gdb/common/ax.def
index fdc043f..b08ede3 100644
--- a/gdb/common/ax.def
+++ b/gdb/common/ax.def
@@ -93,3 +93,5 @@ DEFOP (invalid2, 0, 0, 0, 0, 0x31)
express the right thing. */
DEFOP (pick, 1, 0, 0, 1, 0x32)
DEFOP (rot, 0, 0, 3, 3, 0x33)
+/* Both the argument and consumed numbers are dynamic for this one. */
+DEFOP (printf, 0, 0, 0, 0, 0x34)
diff --git a/gdb/common/format.c b/gdb/common/format.c
new file mode 100644
index 0000000..9c22b6d
--- /dev/null
+++ b/gdb/common/format.c
@@ -0,0 +1,400 @@
+/* Parse a printf-style format string.
+
+ Copyright (C) 1986-2012 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#ifdef GDBSERVER
+#include "server.h"
+#else
+#include "defs.h"
+#endif
+
+#include <string.h>
+
+#include "format.h"
+
+struct format_piece *
+parse_format_string (char **arg)
+{
+ char *s, *f, *string;
+ char *prev_start;
+ char *percent_loc;
+ char *sub_start, *current_substring;
+ struct format_piece *pieces;
+ int next_frag;
+ int max_pieces;
+ enum argclass this_argclass;
+
+ s = *arg;
+
+ /* Parse the format-control string and copy it into the string STRING,
+ processing some kinds of escape sequence. */
+
+ f = string = (char *) alloca (strlen (s) + 1);
+
+ while (*s != '"' && *s != '\0')
+ {
+ int c = *s++;
+ switch (c)
+ {
+ case '\0':
+ continue;
+
+ case '\\':
+ switch (c = *s++)
+ {
+ case '\\':
+ *f++ = '\\';
+ break;
+ case 'a':
+ *f++ = '\a';
+ break;
+ case 'b':
+ *f++ = '\b';
+ break;
+ case 'f':
+ *f++ = '\f';
+ break;
+ case 'n':
+ *f++ = '\n';
+ break;
+ case 'r':
+ *f++ = '\r';
+ break;
+ case 't':
+ *f++ = '\t';
+ break;
+ case 'v':
+ *f++ = '\v';
+ break;
+ case '"':
+ *f++ = '"';
+ break;
+ default:
+ /* ??? TODO: handle other escape sequences. */
+ error (_("Unrecognized escape character \\%c in format string."),
+ c);
+ }
+ break;
+
+ default:
+ *f++ = c;
+ }
+ }
+
+ /* Terminate our escape-processed copy. */
+ *f++ = '\0';
+
+ /* Whether the format string ended with double-quote or zero, we're
+ done with it; it's up to callers to complain about syntax. */
+ *arg = s;
+
+ /* Need extra space for the '\0's. Doubling the size is sufficient. */
+
+ current_substring = xmalloc (strlen (string) * 2 + 1000);
+
+ max_pieces = strlen (string) + 2;
+
+ pieces = (struct format_piece *)
+ xmalloc (max_pieces * sizeof (struct format_piece));
+
+ next_frag = 0;
+
+ /* Now scan the string for %-specs and see what kinds of args they want.
+ argclass classifies the %-specs so we can give printf-type functions
+ something of the right size. */
+
+ f = string;
+ prev_start = string;
+ while (*f)
+ if (*f++ == '%')
+ {
+ int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0;
+ int seen_space = 0, seen_plus = 0;
+ int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
+ int seen_big_d = 0, seen_double_big_d = 0;
+ int bad = 0;
+
+ /* Skip over "%%", it will become part of a literal piece. */
+ if (*f == '%')
+ {
+ f++;
+ continue;
+ }
+
+ sub_start = current_substring;
+
+ strncpy (current_substring, prev_start, f - 1 - prev_start);
+ current_substring += f - 1 - prev_start;
+ *current_substring++ = '\0';
+
+ pieces[next_frag].string = sub_start;
+ pieces[next_frag].argclass = literal_piece;
+ next_frag++;
+
+ percent_loc = f - 1;
+
+ /* Check the validity of the format specifier, and work
+ out what argument it expects. We only accept C89
+ format strings, with the exception of long long (which
+ we autoconf for). */
+
+ /* The first part of a format specifier is a set of flag
+ characters. */
+ while (strchr ("0-+ #", *f))
+ {
+ if (*f == '#')
+ seen_hash = 1;
+ else if (*f == '0')
+ seen_zero = 1;
+ else if (*f == ' ')
+ seen_space = 1;
+ else if (*f == '+')
+ seen_plus = 1;
+ f++;
+ }
+
+ /* The next part of a format specifier is a width. */
+ while (strchr ("0123456789", *f))
+ f++;
+
+ /* The next part of a format specifier is a precision. */
+ if (*f == '.')
+ {
+ seen_prec = 1;
+ f++;
+ while (strchr ("0123456789", *f))
+ f++;
+ }
+
+ /* The next part of a format specifier is a length modifier. */
+ if (*f == 'h')
+ {
+ seen_h = 1;
+ f++;
+ }
+ else if (*f == 'l')
+ {
+ f++;
+ lcount++;
+ if (*f == 'l')
+ {
+ f++;
+ lcount++;
+ }
+ }
+ else if (*f == 'L')
+ {
+ seen_big_l = 1;
+ f++;
+ }
+ /* Decimal32 modifier. */
+ else if (*f == 'H')
+ {
+ seen_big_h = 1;
+ f++;
+ }
+ /* Decimal64 and Decimal128 modifiers. */
+ else if (*f == 'D')
+ {
+ f++;
+
+ /* Check for a Decimal128. */
+ if (*f == 'D')
+ {
+ f++;
+ seen_double_big_d = 1;
+ }
+ else
+ seen_big_d = 1;
+ }
+
+ switch (*f)
+ {
+ case 'u':
+ if (seen_hash)
+ bad = 1;
+ /* FALLTHROUGH */
+
+ case 'o':
+ case 'x':
+ case 'X':
+ if (seen_space || seen_plus)
+ bad = 1;
+ /* FALLTHROUGH */
+
+ case 'd':
+ case 'i':
+ if (lcount == 0)
+ this_argclass = int_arg;
+ else if (lcount == 1)
+ this_argclass = long_arg;
+ else
+ this_argclass = long_long_arg;
+
+ if (seen_big_l)
+ bad = 1;
+ break;
+
+ case 'c':
+ this_argclass = lcount == 0 ? int_arg : wide_char_arg;
+ if (lcount > 1 || seen_h || seen_big_l)
+ bad = 1;
+ if (seen_prec || seen_zero || seen_space || seen_plus)
+ bad = 1;
+ break;
+
+ case 'p':
+ this_argclass = ptr_arg;
+ if (lcount || seen_h || seen_big_l)
+ bad = 1;
+ if (seen_prec || seen_zero || seen_space || seen_plus)
+ bad = 1;
+ break;
+
+ case 's':
+ this_argclass = lcount == 0 ? string_arg : wide_string_arg;
+ if (lcount > 1 || seen_h || seen_big_l)
+ bad = 1;
+ if (seen_zero || seen_space || seen_plus)
+ bad = 1;
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'E':
+ case 'G':
+ if (seen_big_h || seen_big_d || seen_double_big_d)
+ this_argclass = decfloat_arg;
+ else if (seen_big_l)
+ this_argclass = long_double_arg;
+ else
+ this_argclass = double_arg;
+
+ if (lcount || seen_h)
+ bad = 1;
+ break;
+
+ case '*':
+ error (_("`*' not supported for precision or width in printf"));
+
+ case 'n':
+ error (_("Format specifier `n' not supported in printf"));
+
+ case '\0':
+ error (_("Incomplete format specifier at end of format string"));
+
+ default:
+ error (_("Unrecognized format specifier '%c' in printf"), *f);
+ }
+
+ if (bad)
+ error (_("Inappropriate modifiers to "
+ "format specifier '%c' in printf"),
+ *f);
+
+ f++;
+
+ sub_start = current_substring;
+
+ if (lcount > 1 && USE_PRINTF_I64)
+ {
+ /* Windows' printf does support long long, but not the usual way.
+ Convert %lld to %I64d. */
+ int length_before_ll = f - percent_loc - 1 - lcount;
+
+ strncpy (current_substring, percent_loc, length_before_ll);
+ strcpy (current_substring + length_before_ll, "I64");
+ current_substring[length_before_ll + 3] =
+ percent_loc[length_before_ll + lcount];
+ current_substring += length_before_ll + 4;
+ }
+ else if (this_argclass == wide_string_arg
+ || this_argclass == wide_char_arg)
+ {
+ /* Convert %ls or %lc to %s. */
+ int length_before_ls = f - percent_loc - 2;
+
+ strncpy (current_substring, percent_loc, length_before_ls);
+ strcpy (current_substring + length_before_ls, "s");
+ current_substring += length_before_ls + 2;
+ }
+ else
+ {
+ strncpy (current_substring, percent_loc, f - percent_loc);
+ current_substring += f - percent_loc;
+ }
+
+ *current_substring++ = '\0';
+
+ prev_start = f;
+
+ pieces[next_frag].string = sub_start;
+ pieces[next_frag].argclass = this_argclass;
+ next_frag++;
+ }
+
+ /* Record the remainder of the string. */
+
+ sub_start = current_substring;
+
+ strncpy (current_substring, prev_start, f - prev_start);
+ current_substring += f - prev_start;
+ *current_substring++ = '\0';
+
+ pieces[next_frag].string = sub_start;
+ pieces[next_frag].argclass = literal_piece;
+ next_frag++;
+
+ /* Record an end-of-array marker. */
+
+ pieces[next_frag].string = NULL;
+ pieces[next_frag].argclass = literal_piece;
+
+ return pieces;
+}
+
+void
+free_format_pieces (struct format_piece *pieces)
+{
+ if (!pieces)
+ return;
+
+ /* We happen to know that all the string pieces are in the block
+ pointed to by the first string piece. */
+ if (pieces[0].string)
+ xfree (pieces[0].string);
+
+ xfree (pieces);
+}
+
+void
+free_format_pieces_cleanup (void *ptr)
+{
+ void **location = ptr;
+
+ if (location == NULL)
+ return;
+
+ if (*location != NULL)
+ {
+ free_format_pieces (*location);
+ *location = NULL;
+ }
+}
+
diff --git a/gdb/common/format.h b/gdb/common/format.h
new file mode 100644
index 0000000..4c6c1b9
--- /dev/null
+++ b/gdb/common/format.h
@@ -0,0 +1,63 @@
+/* Parse a printf-style format string.
+
+ Copyright (C) 1986-2012 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG)
+# define USE_PRINTF_I64 1
+# define PRINTF_HAS_LONG_LONG
+#else
+# define USE_PRINTF_I64 0
+#endif
+
+/* The argclass represents the general type of data that goes with a
+ format directive; int_arg for %d, long_arg for %l, and so forth.
+ Note that these primarily distinguish types by size and need for
+ special handling, so for instance %u and %x are (at present) also
+ classed as int_arg. */
+
+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
+ };
+
+/* A format piece is a section of the format string that may include a
+ single print directive somewhere in it, and the associated class
+ for the argument. */
+
+struct format_piece
+{
+ char *string;
+ enum argclass argclass;
+};
+
+/* Return an array of printf fragments found at the given string, and
+ rewrite ARG with a pointer to the end of the format string. */
+
+extern struct format_piece *parse_format_string (char **arg);
+
+/* Given a pointer to an array of format pieces, free any memory that
+ would have been allocated by parse_format_string. */
+
+extern void free_format_pieces (struct format_piece *frags);
+
+/* Freeing, cast as a cleanup. */
+
+extern void free_format_pieces_cleanup (void *);
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 1b75b4e..c6ff8f9 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,13 @@
+2012-07-02 Stan Shebs <stan@codesourcery.com>
+
+ * gdb.texinfo (Dynamic Printf): Mention agent style and
+ disconnected dprintf.
+ (Maintenance Commands): Describe maint agent-printf.
+ (General Query Packets): Mention BreakpointCommands feature.
+ (Packets): Document commands extension to Z0 packet.
+ * agentexpr.texi (Bytecode Descriptions): Document printf
+ bytecode.
+
2012-07-02 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.texinfo (File Options): Change -ix and -iex commands that apply
diff --git a/gdb/doc/agentexpr.texi b/gdb/doc/agentexpr.texi
index d0f6f15..1b0a397 100644
--- a/gdb/doc/agentexpr.texi
+++ b/gdb/doc/agentexpr.texi
@@ -493,6 +493,23 @@ Record the bytes at @var{addr} in a trace buffer, for later retrieval
by GDB. Stop at either the first zero byte, or when @var{size} bytes
have been recorded, whichever occurs first.
+@item @code{printf} (0x34) @var{numargs} @var{string} @result{}
+Do a formatted print, in the style of the C function @code{printf}).
+The value of @var{numargs} is the number of arguments to expect on the
+stack, while @var{string} is the format string, prefixed with a
+two-byte length. The last byte of the string must be zero, and is
+included in the length. The format string includes escaped sequences
+just as it appears in C source, so for instance the format string
+@code{"\t%d\n"} is six characters long, and the output will consist of
+a tab character, a decimal number, and a newline. At the top of the
+stack, above the values to be printed, this bytecode will pop a
+``function'' and ``channel''. If the function is nonzero, then the
+target may treat it as a function and call it, passing the channel as
+a first argument, as with the C function @code{fprintf}. If the
+function is zero, then the target may simply call a standard formatted
+print function of its choice. In all, this bytecode pops 2 +
+@var{numargs} stack elements, and pushes nothing.
+
@item @code{end} (0x27): @result{}
Stop executing bytecode; the result should be the top element of the
stack. If the purpose of the expression was to compute an lvalue or a
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 83ce123..d324a93 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4649,6 +4649,14 @@ program's @code{printf} function. This has the advantage that the
characters go to the program's output device, so they can recorded in
redirects to files and so forth.
+If you are doing remote debugging with a stub or agent, you can also
+ask to have the printf handled by the remote agent. In addition to
+ensuring that the output goes to the remote program's device along
+with any other output the program might produce, you can also ask that
+the dprintf remain active even after disconnecting from the remote
+target. Using the stub/agent is also more efficient, as it can do
+everything without needing to communicate with @value{GDBN}.
+
@table @code
@kindex dprintf
@item dprintf @var{location},@var{template},@var{expression}[,@var{expression}@dots{}]
@@ -4672,6 +4680,12 @@ Handle the output using the @value{GDBN} @code{printf} command.
Handle the output by calling a function in your program (normally
@code{printf}).
+@item agent
+@kindex dprintf-style agent
+Have the remote debugging agent (such as @code{gdbserver}) handle
+the output itself. This style is only available for agents that
+support running commands on the target.
+
@item set dprintf-function @var{function}
Set the function to call if the dprintf style is @code{call}. By
default its value is @code{printf}. You may set it to any expression.
@@ -4706,6 +4720,17 @@ Note that the @code{info break} displays the dynamic printf commands
as normal breakpoint commands; you can thus easily see the effect of
the variable settings.
+@item set disconnected-dprintf on
+@itemx set disconnected-dprintf off
+@kindex set disconnected-dprintf
+Choose whether @code{dprintf} commands should continue to run if
+@value{GDBN} has disconnected from the target. This only applies
+if the @code{dprintf-style} is @code{agent}.
+
+@item show disconnected-dprintf off
+@kindex show disconnected-dprintf
+Show the current choice for disconnected @code{dprintf}.
+
@end table
@value{GDBN} does not check the validity of function and channel,
@@ -34447,6 +34472,13 @@ of the addresses of @code{globa} and @code{globb}, while discarding
the result of the addition, while an evaluation expression will do the
addition and return the sum.
+@kindex maint agent-printf
+@item maint agent-printf @var{format},@var{expr},...
+Translate the given format string and list of argument expressions
+into remote agent bytecodes and display them as a disassembled list.
+This command is useful for debugging the agent version of dynamic
+printf (@pxref{Dynamic Printf}.
+
@kindex maint info breakpoints
@item @anchor{maint info breakpoints}maint info breakpoints
Using the same format as @samp{info breakpoints}, display both the
@@ -35681,7 +35713,7 @@ avoid potential problems with duplicate packets, the operations should
be implemented in an idempotent way.}
@item z0,@var{addr},@var{kind}
-@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]}
+@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]}@r{[};cmds:@var{persist},@var{cmd_list}@dots{}@r{]}
@cindex @samp{z0} packet
@cindex @samp{Z0} packet
Insert (@samp{Z0}) or remove (@samp{z0}) a memory breakpoint at address
@@ -35709,6 +35741,22 @@ actual conditional expression in bytecode form.
@end table
+The optional @var{cmd_list} parameter introduces commands that may be
+run on the target, rather than being reported back to @value{GDBN}.
+The parameter starts with a numeric flag @var{persist}; if the flag is
+nonzero, then the breakpoint may remain active and the commands
+continue to be run even when @value{GDBN} disconnects from the target.
+Following this flag is a series of expressions concatenated with no
+separators. Each expression has the following form:
+
+@table @samp
+
+@item X @var{len},@var{expr}
+@var{len} is the length of the bytecode expression and @var{expr} is the
+actual conditional expression in bytecode form.
+
+@end table
+
see @ref{Architecture-Specific Protocol Details}.
@emph{Implementation note: It is possible for a target to copy or move
@@ -36642,6 +36690,11 @@ These are the currently defined stub features and their properties:
@tab @samp{-}
@tab No
+@item @samp{BreakpointCommands}
+@tab No
+@tab @samp{-}
+@tab No
+
@end multitable
These are the currently defined stub features, in more detail:
@@ -36791,6 +36844,11 @@ to be enabled and disabled while a trace experiment is running.
The remote stub supports the @samp{tracenz} bytecode for collecting strings.
See @ref{Bytecode Descriptions} for details about the bytecode.
+@item BreakpointCommands
+@cindex breakpoint commands, in remote protocol
+The remote stub supports running a breakpoint's command list itself,
+rather than reporting the hit to @value{GDBN}.
+
@end table
@item qSymbol::
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 683aade..02a2398 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,31 @@
+2012-07-02 Stan Shebs <stan@codesourcery.com>
+
+ * Makefile.in (WARN_CFLAGS_NO_FORMAT): Define.
+ (ax.o): Add it to build rule.
+ (ax-ipa.o): Ditto.
+ (OBS): Add format.o.
+ (IPA_OBS): Add format.o.
+ * server.c (handle_query): Claim support for breakpoint commands.
+ (process_point_options): Add command case.
+ (process_serial_event): Leave running if there are printfs in
+ effect.
+ * mem-break.h (any_persistent_commands): Declare.
+ (add_breakpoint_commands): Declare.
+ (gdb_no_commands_at_breakpoint): Declare.
+ (run_breakpoint_commands): Declare.
+ * mem-break.c (struct point_command_list): New struct.
+ (struct breakpoint): New field command_list.
+ (any_persistent_commands): New function.
+ (add_commands_to_breakpoint): New function.
+ (add_breakpoint_commands): New function.
+ (gdb_no_commands_at_breakpoint): New function.
+ (run_breakpoint_commands): New function.
+ * linux-low.c (linux_wait_1): Test for and run breakpoint commands
+ locally.
+ * ax.c: Include format.h.
+ (ax_printf): New function.
+ (gdb_eval_agent_expr): Add printf opcode.
+
2012-06-13 Yao Qi <yao@codesourcery.com>
* server.c (start_inferior): Remove duplicated writes to fields
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 3c5c041..446ea04 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -100,6 +100,9 @@ GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS}
WARN_CFLAGS = @WARN_CFLAGS@
WERROR_CFLAGS = @WERROR_CFLAGS@
+WARN_CFLAGS_NO_FORMAT = `echo " $(WARN_CFLAGS) " \
+ | sed "s/ -Wformat-nonliteral / -Wno-format-nonliteral /g"`
+
# CFLAGS is specifically reserved for setting from the command line
# when running make. I.E. "make CFLAGS=-Wmissing-prototypes".
CFLAGS = @CFLAGS@
@@ -152,7 +155,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS}
OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o target.o \
utils.o version.o vec.o \
mem-break.o hostio.o event-loop.o tracepoint.o \
- xml-utils.o common-utils.o ptid.o buffer.o \
+ xml-utils.o common-utils.o ptid.o buffer.o format.o \
dll.o \
$(XML_BUILTIN) \
$(DEPFILES) $(LIBOBJS)
@@ -273,7 +276,7 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS)
${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \
$(XM_CLIBS)
-IPA_OBJS=ax-ipa.o tracepoint-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
+IPA_OBJS=ax-ipa.o tracepoint-ipa.o format-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
IPA_LIB=libinproctrace.so
@@ -447,11 +450,13 @@ IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_CFLAGS) $(UST_CFLAGS) \
# In-process agent object rules
ax-ipa.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
- $(CC) -c $(IPAGENT_CFLAGS) $< -o ax-ipa.o
+ $(CC) -c $(IPAGENT_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $< -o ax-ipa.o
tracepoint-ipa.o: tracepoint.c $(server_h) ${ax_h}
$(CC) -c $(IPAGENT_CFLAGS) $< -o tracepoint-ipa.o
utils-ipa.o: utils.c $(server_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o utils-ipa.o
+format-ipa.o: ../common/format.c $(server_h) ${ax_h}
+ $(CC) -c $(IPAGENT_CFLAGS) $< -o format-ipa.o
common-utils-ipa.o: ../common/common-utils.c $(server_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o common-utils-ipa.o
remote-utils-ipa.o: remote-utils.c $(server_h)
@@ -468,6 +473,7 @@ amd64-linux-ipa.o : amd64-linux.c $(regdef_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o amd64-linux-ipa.o
ax.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
+ $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $<
event-loop.o: event-loop.c $(server_h)
hostio.o: hostio.c $(server_h)
hostio-errno.o: hostio-errno.c $(server_h)
@@ -512,6 +518,9 @@ ptid.o: ../common/ptid.c $(ptid_h)
buffer.o: ../common/buffer.c $(server_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
+format.o: ../common/format.c $(server_h)
+ $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
+
agent.o: ../common/agent.c $(server_h) $(agent_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
diff --git a/gdb/gdbserver/ax.c b/gdb/gdbserver/ax.c
index 4075c26..70e9322 100644
--- a/gdb/gdbserver/ax.c
+++ b/gdb/gdbserver/ax.c
@@ -18,6 +18,7 @@
#include "server.h"
#include "ax.h"
+#include "format.h"
static void ax_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2);
@@ -789,6 +790,123 @@ compile_bytecodes (struct agent_expr *aexpr)
#endif
+/* Make printf-type calls using arguments supplied from the host. We
+ need to parse the format string ourselves, and call the formatting
+ function with one argument at a time, partly because there is no
+ safe portable way to construct a varargs call, and partly to serve
+ as a security barrier against bad format strings that might get
+ in. */
+
+static void
+ax_printf (CORE_ADDR fn, CORE_ADDR chan, char *format,
+ int nargs, ULONGEST *args)
+{
+ char *f = format;
+ struct format_piece *fpieces;
+ int i, fp;
+ char *current_substring;
+ int nargs_wanted;
+
+ ax_debug ("Printf of \"%s\" with %d args", format, nargs);
+
+ fpieces = parse_format_string (&f);
+
+ nargs_wanted = 0;
+ for (fp = 0; fpieces[fp].string != NULL; fp++)
+ if (fpieces[fp].argclass != literal_piece)
+ ++nargs_wanted;
+
+ if (nargs != nargs_wanted)
+ error (_("Wrong number of arguments for specified format-string"));
+
+ i = 0;
+ for (fp = 0; fpieces[fp].string != NULL; fp++)
+ {
+ current_substring = fpieces[fp].string;
+ ax_debug ("current substring is '%s', class is %d",
+ current_substring, fpieces[fp].argclass);
+ switch (fpieces[fp].argclass)
+ {
+ case string_arg:
+ {
+ gdb_byte *str;
+ CORE_ADDR tem;
+ int j;
+
+ tem = args[i];
+
+ /* This is a %s argument. Find the length of the string. */
+ for (j = 0;; j++)
+ {
+ gdb_byte c;
+
+ read_inferior_memory (tem + j, &c, 1);
+ if (c == 0)
+ break;
+ }
+
+ /* Copy the string contents into a string inside GDB. */
+ str = (gdb_byte *) alloca (j + 1);
+ if (j != 0)
+ read_inferior_memory (tem, str, j);
+ str[j] = 0;
+
+ printf (current_substring, (char *) str);
+ }
+ break;
+
+ case long_long_arg:
+#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG)
+ {
+ long long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+#else
+ error (_("long long not supported in agent printf"));
+#endif
+ case int_arg:
+ {
+ int val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case long_arg:
+ {
+ long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case literal_piece:
+ /* Print a portion of the format string that has no
+ directives. Note that this will not include any
+ ordinary %-specs, but it might include "%%". That is
+ why we use printf_filtered and not puts_filtered here.
+ Also, we pass a dummy argument because some platforms
+ have modified GCC to include -Wformat-security by
+ default, which will warn here if there is no
+ argument. */
+ printf (current_substring, 0);
+ break;
+
+ default:
+ error (_("Format directive in '%s' not supported in agent printf"),
+ current_substring);
+ }
+
+ /* Maybe advance to the next argument. */
+ if (fpieces[fp].argclass != literal_piece)
+ ++i;
+ }
+
+ free_format_pieces (fpieces);
+}
+
/* The agent expression evaluator, as specified by the GDB docs. It
returns 0 if everything went OK, and a nonzero error code
otherwise. */
@@ -1152,6 +1270,43 @@ gdb_eval_agent_expr (struct regcache *regcache,
top = stack[sp];
break;
+ case gdb_agent_op_printf:
+ {
+ int nargs, slen, i;
+ CORE_ADDR fn = 0, chan = 0;
+ /* Can't have more args than the entire size of the stack. */
+ ULONGEST args[STACK_MAX];
+ char *format;
+
+ nargs = aexpr->bytes[pc++];
+ slen = aexpr->bytes[pc++];
+ slen = (slen << 8) + aexpr->bytes[pc++];
+ format = (char *) &(aexpr->bytes[pc]);
+ pc += slen;
+ /* Pop function and channel. */
+ fn = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ chan = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ /* Pop arguments into a dedicated array. */
+ for (i = 0; i < nargs; ++i)
+ {
+ args[i] = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ }
+
+ /* A bad format string means something is very wrong; give
+ up immediately. */
+ if (format[slen - 1] != '\0')
+ error (_("Unterminated format string in printf bytecode"));
+
+ ax_printf (fn, chan, format, nargs, args);
+ }
+ break;
+
/* GDB never (currently) generates any of these ops. */
case gdb_agent_op_float:
case gdb_agent_op_ref_float:
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3d116fd..48134c3 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2618,7 +2618,10 @@ Check if we're already there.\n",
|| (!step_over_finished
&& !bp_explains_trap && !trace_event)
|| (gdb_breakpoint_here (event_child->stop_pc)
- && gdb_condition_true_at_breakpoint (event_child->stop_pc)));
+ && gdb_condition_true_at_breakpoint (event_child->stop_pc)
+ && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
+
+ run_breakpoint_commands (event_child->stop_pc);
/* We found no reason GDB would want us to stop. We either hit one
of our own breakpoints, or finished an internal step GDB
@@ -3499,7 +3502,8 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy)
though. If the condition is being evaluated on the target's side
and it evaluate to false, step over this breakpoint as well. */
if (gdb_breakpoint_here (pc)
- && gdb_condition_true_at_breakpoint (pc))
+ && gdb_condition_true_at_breakpoint (pc)
+ && gdb_no_commands_at_breakpoint (pc))
{
if (debug_threads)
fprintf (stderr,
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 6b6b25c..aec09ba 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -97,6 +97,20 @@ struct point_cond_list
struct point_cond_list *next;
};
+struct point_command_list
+{
+ /* Pointer to the agent expression that is the breakpoint's
+ commands. */
+ struct agent_expr *cmd;
+
+ /* Flag that is true if this command should run even while GDB is
+ disconnected. */
+ int persistence;
+
+ /* Pointer to the next command. */
+ struct point_command_list *next;
+};
+
/* A high level (in gdbserver's perspective) breakpoint. */
struct breakpoint
{
@@ -111,6 +125,9 @@ struct breakpoint
target's side. */
struct point_cond_list *cond_list;
+ /* Point to the list of commands to run when this is hit. */
+ struct point_command_list *command_list;
+
/* Link to this breakpoint's raw breakpoint. This is always
non-NULL. */
struct raw_breakpoint *raw;
@@ -121,6 +138,23 @@ struct breakpoint
int (*handler) (CORE_ADDR);
};
+int
+any_persistent_commands ()
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+ struct point_command_list *cl;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ for (cl = bp->command_list; cl != NULL; cl = cl->next)
+ if (cl->persistence)
+ return 1;
+ }
+
+ return 0;
+}
+
static struct raw_breakpoint *
find_raw_breakpoint_at (CORE_ADDR where)
{
@@ -835,6 +869,97 @@ gdb_condition_true_at_breakpoint (CORE_ADDR where)
return (value != 0);
}
+/* Add commands COMMANDS to GDBserver's breakpoint BP. */
+
+void
+add_commands_to_breakpoint (struct breakpoint *bp,
+ struct agent_expr *commands, int persist)
+{
+ struct point_command_list *new_cmd;
+
+ /* Create new command. */
+ new_cmd = xcalloc (1, sizeof (*new_cmd));
+ new_cmd->cmd = commands;
+ new_cmd->persistence = persist;
+
+ /* Add commands to the list. */
+ new_cmd->next = bp->command_list;
+ bp->command_list = new_cmd;
+}
+
+/* Add a target-side command COMMAND to the breakpoint at ADDR. */
+
+int
+add_breakpoint_commands (CORE_ADDR addr, char **command, int persist)
+{
+ struct breakpoint *bp = find_gdb_breakpoint_at (addr);
+ char *actparm = *command;
+ struct agent_expr *cmd;
+
+ if (bp == NULL)
+ return 1;
+
+ if (command == NULL)
+ return 1;
+
+ cmd = gdb_parse_agent_expr (&actparm);
+
+ if (cmd == NULL)
+ {
+ fprintf (stderr, "Command evaluation failed. "
+ "Disabling.\n");
+ return 0;
+ }
+
+ add_commands_to_breakpoint (bp, cmd, persist);
+
+ *command = actparm;
+
+ return 0;
+}
+
+/* Return true if there are no commands to run at this location,
+ which likely means we want to report back to GDB. */
+int
+gdb_no_commands_at_breakpoint (CORE_ADDR where)
+{
+ struct breakpoint *bp = find_gdb_breakpoint_at (where);
+
+ if (bp == NULL)
+ return 0;
+
+ if (debug_threads)
+ fprintf (stderr, "at 0x%s, bp command_list is 0x%x\n",
+ paddress (where), (int) bp->command_list);
+ return (bp->command_list == NULL);
+}
+
+void
+run_breakpoint_commands (CORE_ADDR where)
+{
+ /* Fetch registers for the current inferior. */
+ struct breakpoint *bp = find_gdb_breakpoint_at (where);
+ ULONGEST value = 0;
+ struct point_command_list *cl;
+ int err = 0;
+
+ struct regcache *regcache = get_thread_regcache (current_inferior, 1);
+
+ if (bp == NULL)
+ return;
+
+ for (cl = bp->command_list;
+ cl && !value && !err; cl = cl->next)
+ {
+ /* Run the command. */
+ err = gdb_eval_agent_expr (regcache, NULL, cl->cmd, &value);
+
+ /* If one command has a problem, stop digging the hole deeper. */
+ if (err)
+ break;
+ }
+}
+
/* Return 1 if there is a breakpoint inserted in address WHERE
and if its condition, if it exists, is true. */
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index 95b7f9d..bb2aa03 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -52,11 +52,19 @@ void clear_gdb_breakpoint_conditions (CORE_ADDR addr);
int add_breakpoint_condition (CORE_ADDR addr, char **condition);
+int add_breakpoint_commands (CORE_ADDR addr, char **commands, int persist);
+
+int any_persistent_commands (void);
+
/* Evaluation condition (if any) at breakpoint BP. Return 1 if
true and 0 otherwise. */
int gdb_condition_true_at_breakpoint (CORE_ADDR where);
+int gdb_no_commands_at_breakpoint (CORE_ADDR where);
+
+void run_breakpoint_commands (CORE_ADDR where);
+
/* Returns TRUE if there's a GDB breakpoint set at ADDR. */
int gdb_breakpoint_here (CORE_ADDR where);
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 69b54d9..963d575 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1685,8 +1685,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
strcat (own_buf, ";tracenz+");
}
- /* Support target-side breakpoint conditions. */
+ /* Support target-side breakpoint conditions and commands. */
strcat (own_buf, ";ConditionalBreakpoints+");
+ strcat (own_buf, ";BreakpointCommands+");
if (target_supports_agent ())
strcat (own_buf, ";QAgent+");
@@ -2907,6 +2908,7 @@ static void
process_point_options (CORE_ADDR point_addr, char **packet)
{
char *dataptr = *packet;
+ int persist;
/* Check if data has the correct format. */
if (*dataptr != ';')
@@ -2916,22 +2918,33 @@ process_point_options (CORE_ADDR point_addr, char **packet)
while (*dataptr)
{
- switch (*dataptr)
+ if (*dataptr == ';')
+ ++dataptr;
+
+ if (*dataptr == 'X')
{
- case 'X':
- /* Conditional expression. */
- if (remote_debug)
- fprintf (stderr, "Found breakpoint condition.\n");
- add_breakpoint_condition (point_addr, &dataptr);
- break;
- default:
- /* Unrecognized token, just skip it. */
- fprintf (stderr, "Unknown token %c, ignoring.\n",
- *dataptr);
+ /* Conditional expression. */
+ fprintf (stderr, "Found breakpoint condition.\n");
+ add_breakpoint_condition (point_addr, &dataptr);
+ }
+ else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0)
+ {
+ dataptr += strlen ("cmds:");
+ if (debug_threads)
+ fprintf (stderr, "Found breakpoint commands %s.\n", dataptr);
+ persist = (*dataptr == '1');
+ dataptr += 2;
+ add_breakpoint_commands (point_addr, &dataptr, persist);
+ }
+ else
+ {
+ /* Unrecognized token, just skip it. */
+ fprintf (stderr, "Unknown token %c, ignoring.\n",
+ *dataptr);
}
/* Skip tokens until we find one that we recognize. */
- while (*dataptr && *dataptr != 'X' && *dataptr != ';')
+ while (*dataptr && *dataptr != ';')
dataptr++;
}
*packet = dataptr;
@@ -2997,7 +3010,7 @@ process_serial_event (void)
pid =
ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id);
- if (tracing && disconnected_tracing)
+ if ((tracing && disconnected_tracing) || any_persistent_commands ())
{
struct thread_resume resume_info;
struct process_info *process = find_process_pid (pid);
@@ -3008,9 +3021,15 @@ process_serial_event (void)
break;
}
- fprintf (stderr,
- "Disconnected tracing in effect, "
- "leaving gdbserver attached to the process\n");
+ if (tracing && disconnected_tracing)
+ fprintf (stderr,
+ "Disconnected tracing in effect, "
+ "leaving gdbserver attached to the process\n");
+
+ if (any_persistent_commands ())
+ fprintf (stderr,
+ "Persistent commands are present, "
+ "leaving gdbserver attached to the process\n");
/* Make sure we're in non-stop/async mode, so we we can both
wait for an async socket accept, and handle async target
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 030a4f2..2a0a886 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -49,18 +49,12 @@
#include "charset.h"
#include "arch-utils.h"
#include "cli/cli-utils.h"
+#include "format.h"
#ifdef TUI
#include "tui/tui.h" /* For tui_active et al. */
#endif
-#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG)
-# define USE_PRINTF_I64 1
-# define PRINTF_HAS_LONG_LONG
-#else
-# define USE_PRINTF_I64 0
-#endif
-
struct format_data
{
int count;
@@ -2001,13 +1995,9 @@ print_variable_and_value (const char *name, struct symbol *var,
static void
ui_printf (char *arg, struct ui_file *stream)
{
- char *f = NULL;
+ struct format_piece *fpieces;
char *s = arg;
- char *string = NULL;
struct value **val_args;
- char *substrings;
- char *current_substring;
- int nargs = 0;
int allocated_args = 20;
struct cleanup *old_cleanups;
@@ -2023,64 +2013,13 @@ ui_printf (char *arg, struct ui_file *stream)
if (*s++ != '"')
error (_("Bad format string, missing '\"'."));
- /* Parse the format-control string and copy it into the string STRING,
- processing some kinds of escape sequence. */
-
- f = string = (char *) alloca (strlen (s) + 1);
-
- while (*s != '"')
- {
- int c = *s++;
- switch (c)
- {
- case '\0':
- error (_("Bad format string, non-terminated '\"'."));
+ fpieces = parse_format_string (&s);
- case '\\':
- switch (c = *s++)
- {
- case '\\':
- *f++ = '\\';
- break;
- case 'a':
- *f++ = '\a';
- break;
- case 'b':
- *f++ = '\b';
- break;
- case 'f':
- *f++ = '\f';
- break;
- case 'n':
- *f++ = '\n';
- break;
- case 'r':
- *f++ = '\r';
- break;
- case 't':
- *f++ = '\t';
- break;
- case 'v':
- *f++ = '\v';
- break;
- case '"':
- *f++ = '"';
- break;
- default:
- /* ??? TODO: handle other escape sequences. */
- error (_("Unrecognized escape character \\%c in format string."),
- c);
- }
- break;
-
- default:
- *f++ = c;
- }
- }
+ make_cleanup (free_format_pieces_cleanup, &fpieces);
- /* Skip over " and following space and comma. */
- s++;
- *f++ = '\0';
+ if (*s++ != '"')
+ error (_("Bad format string, non-terminated '\"'."));
+
s = skip_spaces (s);
if (*s != ',' && *s != 0)
@@ -2090,240 +2029,16 @@ ui_printf (char *arg, struct ui_file *stream)
s++;
s = skip_spaces (s);
- /* Need extra space for the '\0's. Doubling the size is sufficient. */
- substrings = alloca (strlen (string) * 2);
- current_substring = substrings;
-
{
- /* Now scan the string for %-specs and see what kinds of args they want.
- argclass[I] classifies the %-specs so we can give printf_filtered
- something of the right size. */
-
- enum argclass
- {
- int_arg, long_arg, long_long_arg, ptr_arg,
- string_arg, wide_string_arg, wide_char_arg,
- double_arg, long_double_arg, decfloat_arg
- };
- enum argclass *argclass;
- enum argclass this_argclass;
- char *last_arg;
+ int nargs = 0;
int nargs_wanted;
- int i;
+ int i, fr;
+ char *current_substring;
- argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass);
nargs_wanted = 0;
- f = string;
- last_arg = string;
- while (*f)
- if (*f++ == '%')
- {
- int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0;
- int seen_space = 0, seen_plus = 0;
- int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
- int seen_big_d = 0, seen_double_big_d = 0;
- int bad = 0;
-
- /* Check the validity of the format specifier, and work
- out what argument it expects. We only accept C89
- format strings, with the exception of long long (which
- we autoconf for). */
-
- /* Skip over "%%". */
- if (*f == '%')
- {
- f++;
- continue;
- }
-
- /* The first part of a format specifier is a set of flag
- characters. */
- while (strchr ("0-+ #", *f))
- {
- if (*f == '#')
- seen_hash = 1;
- else if (*f == '0')
- seen_zero = 1;
- else if (*f == ' ')
- seen_space = 1;
- else if (*f == '+')
- seen_plus = 1;
- f++;
- }
-
- /* The next part of a format specifier is a width. */
- while (strchr ("0123456789", *f))
- f++;
-
- /* The next part of a format specifier is a precision. */
- if (*f == '.')
- {
- seen_prec = 1;
- f++;
- while (strchr ("0123456789", *f))
- f++;
- }
-
- /* The next part of a format specifier is a length modifier. */
- if (*f == 'h')
- {
- seen_h = 1;
- f++;
- }
- else if (*f == 'l')
- {
- f++;
- lcount++;
- if (*f == 'l')
- {
- f++;
- lcount++;
- }
- }
- else if (*f == 'L')
- {
- seen_big_l = 1;
- f++;
- }
- /* Decimal32 modifier. */
- else if (*f == 'H')
- {
- seen_big_h = 1;
- f++;
- }
- /* Decimal64 and Decimal128 modifiers. */
- else if (*f == 'D')
- {
- f++;
-
- /* Check for a Decimal128. */
- if (*f == 'D')
- {
- f++;
- seen_double_big_d = 1;
- }
- else
- seen_big_d = 1;
- }
-
- switch (*f)
- {
- case 'u':
- if (seen_hash)
- bad = 1;
- /* FALLTHROUGH */
-
- case 'o':
- case 'x':
- case 'X':
- if (seen_space || seen_plus)
- bad = 1;
- /* FALLTHROUGH */
-
- case 'd':
- case 'i':
- if (lcount == 0)
- this_argclass = int_arg;
- else if (lcount == 1)
- this_argclass = long_arg;
- else
- this_argclass = long_long_arg;
-
- if (seen_big_l)
- bad = 1;
- break;
-
- case 'c':
- this_argclass = lcount == 0 ? int_arg : wide_char_arg;
- if (lcount > 1 || seen_h || seen_big_l)
- bad = 1;
- if (seen_prec || seen_zero || seen_space || seen_plus)
- bad = 1;
- break;
-
- case 'p':
- this_argclass = ptr_arg;
- if (lcount || seen_h || seen_big_l)
- bad = 1;
- if (seen_prec || seen_zero || seen_space || seen_plus)
- bad = 1;
- break;
-
- case 's':
- this_argclass = lcount == 0 ? string_arg : wide_string_arg;
- if (lcount > 1 || seen_h || seen_big_l)
- bad = 1;
- if (seen_zero || seen_space || seen_plus)
- bad = 1;
- break;
-
- case 'e':
- case 'f':
- case 'g':
- case 'E':
- case 'G':
- if (seen_big_h || seen_big_d || seen_double_big_d)
- this_argclass = decfloat_arg;
- else if (seen_big_l)
- this_argclass = long_double_arg;
- else
- this_argclass = double_arg;
-
- if (lcount || seen_h)
- bad = 1;
- break;
-
- case '*':
- error (_("`*' not supported for precision or width in printf"));
-
- case 'n':
- error (_("Format specifier `n' not supported in printf"));
-
- case '\0':
- error (_("Incomplete format specifier at end of format string"));
-
- default:
- error (_("Unrecognized format specifier '%c' in printf"), *f);
- }
-
- if (bad)
- error (_("Inappropriate modifiers to "
- "format specifier '%c' in printf"),
- *f);
-
- f++;
-
- if (lcount > 1 && USE_PRINTF_I64)
- {
- /* Windows' printf does support long long, but not the usual way.
- Convert %lld to %I64d. */
- int length_before_ll = f - last_arg - 1 - lcount;
-
- strncpy (current_substring, last_arg, length_before_ll);
- strcpy (current_substring + length_before_ll, "I64");
- current_substring[length_before_ll + 3] =
- last_arg[length_before_ll + lcount];
- current_substring += length_before_ll + 4;
- }
- else if (this_argclass == wide_string_arg
- || this_argclass == wide_char_arg)
- {
- /* Convert %ls or %lc to %s. */
- int length_before_ls = f - last_arg - 2;
-
- strncpy (current_substring, last_arg, length_before_ls);
- strcpy (current_substring + length_before_ls, "s");
- current_substring += length_before_ls + 2;
- }
- else
- {
- strncpy (current_substring, last_arg, f - last_arg);
- current_substring += f - last_arg;
- }
- *current_substring++ = '\0';
- last_arg = f;
- argclass[nargs_wanted++] = this_argclass;
- }
+ for (fr = 0; fpieces[fr].string != NULL; fr++)
+ if (fpieces[fr].argclass != literal_piece)
+ ++nargs_wanted;
/* Now, parse all arguments and evaluate them.
Store the VALUEs in VAL_ARGS. */
@@ -2349,10 +2064,11 @@ ui_printf (char *arg, struct ui_file *stream)
error (_("Wrong number of arguments for specified format-string"));
/* Now actually print them. */
- current_substring = substrings;
- for (i = 0; i < nargs; i++)
+ i = 0;
+ for (fr = 0; fpieces[fr].string != NULL; fr++)
{
- switch (argclass[i])
+ current_substring = fpieces[fr].string;
+ switch (fpieces[fr].argclass)
{
case string_arg:
{
@@ -2687,20 +2403,25 @@ ui_printf (char *arg, struct ui_file *stream)
break;
}
+ case literal_piece:
+ /* Print a portion of the format string that has no
+ directives. Note that this will not include any
+ ordinary %-specs, but it might include "%%". That is
+ why we use printf_filtered and not puts_filtered here.
+ Also, we pass a dummy argument because some platforms
+ have modified GCC to include -Wformat-security by
+ default, which will warn here if there is no
+ argument. */
+ fprintf_filtered (stream, current_substring, 0);
+ break;
default:
internal_error (__FILE__, __LINE__,
_("failed internal consistency check"));
}
- /* Skip to the next substring. */
- current_substring += strlen (current_substring) + 1;
+ /* Maybe advance to the next argument. */
+ if (fpieces[fr].argclass != literal_piece)
+ ++i;
}
- /* Print the portion of the format string after the last argument.
- Note that this will not include any ordinary %-specs, but it
- might include "%%". That is why we use printf_filtered and not
- puts_filtered here. Also, we pass a dummy argument because
- some platforms have modified GCC to include -Wformat-security
- by default, which will warn here if there is no argument. */
- fprintf_filtered (stream, last_arg, 0);
}
do_cleanups (old_cleanups);
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 01e3f30..1c9367d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -242,6 +242,8 @@ static void remote_console_output (char *msg);
static int remote_supports_cond_breakpoints (void);
+static int remote_can_run_breakpoint_commands (void);
+
/* The non-stop remote protocol provisions for one pending stop reply.
This is where we keep it until it is acknowledged. */
@@ -323,6 +325,10 @@ struct remote_state
conditions. */
int cond_breakpoints;
+ /* True if the stub reports support for target-side breakpoint
+ commands. */
+ int breakpoint_commands;
+
/* True if the stub reports support for fast tracepoints. */
int fast_tracepoints;
@@ -1274,6 +1280,7 @@ enum {
PACKET_qAttached,
PACKET_ConditionalTracepoints,
PACKET_ConditionalBreakpoints,
+ PACKET_BreakpointCommands,
PACKET_FastTracepoints,
PACKET_StaticTracepoints,
PACKET_InstallInTrace,
@@ -3801,6 +3808,16 @@ remote_cond_breakpoint_feature (const struct protocol_feature *feature,
}
static void
+remote_breakpoint_commands_feature (const struct protocol_feature *feature,
+ enum packet_support support,
+ const char *value)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ rs->breakpoint_commands = (support == PACKET_ENABLE);
+}
+
+static void
remote_fast_tracepoint_feature (const struct protocol_feature *feature,
enum packet_support support,
const char *value)
@@ -3898,6 +3915,8 @@ static struct protocol_feature remote_protocol_features[] = {
PACKET_ConditionalTracepoints },
{ "ConditionalBreakpoints", PACKET_DISABLE, remote_cond_breakpoint_feature,
PACKET_ConditionalBreakpoints },
+ { "BreakpointCommands", PACKET_DISABLE, remote_breakpoint_commands_feature,
+ PACKET_BreakpointCommands },
{ "FastTracepoints", PACKET_DISABLE, remote_fast_tracepoint_feature,
PACKET_FastTracepoints },
{ "StaticTracepoints", PACKET_DISABLE, remote_static_tracepoint_feature,
@@ -7873,6 +7892,37 @@ remote_add_target_side_condition (struct gdbarch *gdbarch,
return 0;
}
+static void
+remote_add_target_side_commands (struct gdbarch *gdbarch,
+ struct bp_target_info *bp_tgt, char *buf)
+{
+ struct agent_expr *aexpr = NULL;
+ int i, ix;
+
+ if (VEC_empty (agent_expr_p, bp_tgt->tcommands))
+ return;
+
+ buf += strlen (buf);
+
+ sprintf (buf, ";cmds:%x,", bp_tgt->persist);
+ buf += strlen (buf);
+
+ /* Concatenate all the agent expressions that are commands into the
+ cmds parameter. */
+ for (ix = 0;
+ VEC_iterate (agent_expr_p, bp_tgt->tcommands, ix, aexpr);
+ ix++)
+ {
+ sprintf (buf, "X%x,", aexpr->len);
+ buf += strlen (buf);
+ for (i = 0; i < aexpr->len; ++i)
+ buf = pack_hex_byte (buf, aexpr->buf[i]);
+ *buf = '\0';
+ }
+
+ VEC_free (agent_expr_p, bp_tgt->tcommands);
+}
+
/* Insert a breakpoint. On targets that have software breakpoint
support, we ask the remote target to do the work; on targets
which don't, we insert a traditional memory breakpoint. */
@@ -7910,6 +7960,9 @@ remote_insert_breakpoint (struct gdbarch *gdbarch,
if (remote_supports_cond_breakpoints ())
remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf);
+ if (remote_can_run_breakpoint_commands ())
+ remote_add_target_side_commands (gdbarch, bp_tgt, p);
+
putpkt (rs->buf);
getpkt (&rs->buf, &rs->buf_size, 0);
@@ -8151,6 +8204,9 @@ remote_insert_hw_breakpoint (struct gdbarch *gdbarch,
if (remote_supports_cond_breakpoints ())
remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf);
+ if (remote_can_run_breakpoint_commands ())
+ remote_add_target_side_commands (gdbarch, bp_tgt, p);
+
putpkt (rs->buf);
getpkt (&rs->buf, &rs->buf_size, 0);
@@ -10089,6 +10145,14 @@ remote_supports_string_tracing (void)
return rs->string_tracing;
}
+static int
+remote_can_run_breakpoint_commands (void)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ return rs->breakpoint_commands;
+}
+
static void
remote_trace_init (void)
{
@@ -11007,6 +11071,7 @@ Specify the serial device it is connected to\n\
remote_ops.to_supports_enable_disable_tracepoint = remote_supports_enable_disable_tracepoint;
remote_ops.to_supports_string_tracing = remote_supports_string_tracing;
remote_ops.to_supports_evaluation_of_breakpoint_conditions = remote_supports_cond_breakpoints;
+ remote_ops.to_can_run_breakpoint_commands = remote_can_run_breakpoint_commands;
remote_ops.to_trace_init = remote_trace_init;
remote_ops.to_download_tracepoint = remote_download_tracepoint;
remote_ops.to_can_download_tracepoint = remote_can_download_tracepoint;
@@ -11537,6 +11602,10 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
"ConditionalBreakpoints",
"conditional-breakpoints", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_BreakpointCommands],
+ "BreakpointCommands",
+ "breakpoint-commands", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_FastTracepoints],
"FastTracepoints", "fast-tracepoints", 0);
diff --git a/gdb/target.c b/gdb/target.c
index fe1f9a1..bb8eae8 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -703,6 +703,7 @@ update_current_target (void)
INHERIT (to_can_use_agent, t);
INHERIT (to_magic, t);
INHERIT (to_supports_evaluation_of_breakpoint_conditions, t);
+ INHERIT (to_can_run_breakpoint_commands, t);
/* Do not inherit to_memory_map. */
/* Do not inherit to_flash_erase. */
/* Do not inherit to_flash_done. */
@@ -932,6 +933,9 @@ update_current_target (void)
de_fault (to_supports_evaluation_of_breakpoint_conditions,
(int (*) (void))
return_zero);
+ de_fault (to_can_run_breakpoint_commands,
+ (int (*) (void))
+ return_zero);
de_fault (to_use_agent,
(int (*) (int))
tcomplain);
diff --git a/gdb/target.h b/gdb/target.h
index e5bdf4d..54c58d6 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -672,6 +672,10 @@ struct target_ops
end? */
int (*to_supports_evaluation_of_breakpoint_conditions) (void);
+ /* Does this target support evaluation of breakpoint commands on its
+ end? */
+ int (*to_can_run_breakpoint_commands) (void);
+
/* Determine current architecture of thread PTID.
The target is supposed to determine the architecture of the code where
@@ -997,6 +1001,12 @@ int target_supports_disable_randomization (void);
#define target_supports_evaluation_of_breakpoint_conditions() \
(*current_target.to_supports_evaluation_of_breakpoint_conditions) ()
+/* Returns true if this target can handle breakpoint commands
+ on its end. */
+
+#define target_can_run_breakpoint_commands() \
+ (*current_target.to_can_run_breakpoint_commands) ()
+
/* Invalidate all target dcaches. */
extern void target_dcache_invalidate (void);
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 44673d4..01cc53b 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2012-07-02 Stan Shebs <stan@codesourcery.com>
+
+ * gdb.base/dprintf.exp: Add agent style tests.
+
2012-07-02 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.base/stale-infcall.c (infcall): New label test-next.
diff --git a/gdb/testsuite/gdb.base/dprintf.exp b/gdb/testsuite/gdb.base/dprintf.exp
index 6d1ce6c..1e9d017 100644
--- a/gdb/testsuite/gdb.base/dprintf.exp
+++ b/gdb/testsuite/gdb.base/dprintf.exp
@@ -85,6 +85,30 @@ if ![target_info exists gdb,noinferiorio] {
"2nd dprintf, fprintf"
}
+set target_can_dprintf 1
+set msg "Set dprintf style to agent"
+gdb_test_multiple "set dprintf-style agent" $msg {
+ -re "warning: Target cannot run dprintf commands.*" {
+ set target_can_dprintf 0
+ pass "$msg - cannot do"
+ }
+ -re ".*$gdb_prompt $" {
+ pass "$msg - can do"
+ }
+}
+
+if $target_can_dprintf {
+
+ gdb_run_cmd
+
+ gdb_test "" "Breakpoint"
+
+ gdb_test "continue" "Breakpoint \[0-9\]+, foo .*" "1st dprintf, agent"
+
+ gdb_test "continue" "Breakpoint \[0-9\]+, foo .*" "2nd dprintf, agent"
+
+}
+
gdb_test "set dprintf-style foobar" "Undefined item: \"foobar\"." \
"Set dprintf style to an unrecognized type"