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.
This commit is contained in:
Stan Shebs 2012-07-02 15:29:39 +00:00
parent a47edf2745
commit d3ce09f5bf
28 changed files with 1619 additions and 384 deletions

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)

400
gdb/common/format.c Normal file
View File

@ -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;
}
}

63
gdb/common/format.h Normal file
View File

@ -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 *);

View File

@ -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

View File

@ -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

View File

@ -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::

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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. */

View File

@ -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);

View File

@ -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

View File

@ -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. */
fpieces = parse_format_string (&s);
f = string = (char *) alloca (strlen (s) + 1);
make_cleanup (free_format_pieces_cleanup, &fpieces);
while (*s != '"')
{
int c = *s++;
switch (c)
{
case '\0':
error (_("Bad format string, non-terminated '\"'."));
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;
}
}
/* 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);
}

View File

@ -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,
@ -3800,6 +3807,16 @@ remote_cond_breakpoint_feature (const struct protocol_feature *feature,
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,
@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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.

View File

@ -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"