* 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.
+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.
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
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.
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
$(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)
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
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
#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
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)
{
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 ();
+}
\f
/* Initialization code. */
_("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);
}
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;
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';
+}
\f
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;
/* 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);
\f
/* Functions for printing out expressions, and otherwise debugging
#include "ax-gdb.h"
#include "dummy-frame.h"
+#include "format.h"
+
/* readline include files */
#include "readline/readline.h"
#include "readline/history.h"
/* 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
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.
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;
}
if (bl->pspace != inf->pspace)
continue;
+ if (bl->owner->type == bp_dprintf)
+ continue;
+
if (bl->inserted)
{
val = remove_breakpoint (bl, mark_uninserted);
}
}
+ 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)
{
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. */
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"));
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."));
{
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;
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. */
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);
/* 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
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
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)
--- /dev/null
+/* 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;
+ }
+}
+
--- /dev/null
+/* 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 *);
+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
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
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{}]
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.
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,
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
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
@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
@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:
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::
+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
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@
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)
${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
# 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)
$(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)
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
#include "server.h"
#include "ax.h"
+#include "format.h"
static void ax_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2);
#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. */
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:
|| (!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
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,
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
{
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;
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)
{
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. */
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);
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+");
process_point_options (CORE_ADDR point_addr, char **packet)
{
char *dataptr = *packet;
+ int persist;
/* Check if data has the correct format. */
if (*dataptr != ';')
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;
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);
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
#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;
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;
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)
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. */
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:
{
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);
}
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. */
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;
PACKET_qAttached,
PACKET_ConditionalTracepoints,
PACKET_ConditionalBreakpoints,
+ PACKET_BreakpointCommands,
PACKET_FastTracepoints,
PACKET_StaticTracepoints,
PACKET_InstallInTrace,
rs->cond_breakpoints = (support == PACKET_ENABLE);
}
+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,
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,
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. */
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);
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);
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)
{
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;
"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);
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. */
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);
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
#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);
+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.
"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"