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:
parent
a47edf2745
commit
d3ce09f5bf
28 changed files with 1619 additions and 384 deletions
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
14
gdb/NEWS
14
gdb/NEWS
|
@ -244,11 +244,12 @@ set debug auto-load on|off
|
|||
show debug auto-load
|
||||
Control display of debugging info for auto-loading the files above.
|
||||
|
||||
set dprintf-style gdb|call
|
||||
set dprintf-style gdb|call|agent
|
||||
show dprintf-style
|
||||
Control the way in which a dynamic printf is performed; "gdb" requests
|
||||
a GDB printf command, while "call" causes dprintf to call a function
|
||||
in the inferior.
|
||||
Control the way in which a dynamic printf is performed; "gdb"
|
||||
requests a GDB printf command, while "call" causes dprintf to call a
|
||||
function in the inferior. "agent" requests that the target agent
|
||||
(such as GDBserver) do the printing.
|
||||
|
||||
set dprintf-function <expr>
|
||||
show dprintf-function
|
||||
|
@ -257,6 +258,11 @@ show dprintf-channel
|
|||
Set the function and optional first argument to the call when using
|
||||
the "call" style of dynamic printf.
|
||||
|
||||
set disconnected-dprintf on|off
|
||||
show disconnected-dprintf
|
||||
Control whether agent-style dynamic printfs continue to be in effect
|
||||
after GDB disconnects.
|
||||
|
||||
* New configure options
|
||||
|
||||
--with-auto-load-dir
|
||||
|
|
143
gdb/ax-gdb.c
143
gdb/ax-gdb.c
|
@ -41,10 +41,13 @@
|
|||
#include "tracepoint.h"
|
||||
#include "cp-support.h"
|
||||
#include "arch-utils.h"
|
||||
#include "cli/cli-utils.h"
|
||||
|
||||
#include "valprint.h"
|
||||
#include "c-lang.h"
|
||||
|
||||
#include "format.h"
|
||||
|
||||
/* To make sense of this file, you should read doc/agentexpr.texi.
|
||||
Then look at the types and enums in ax-gdb.h. For the code itself,
|
||||
look at gen_expr, towards the bottom; that's the main function that
|
||||
|
@ -2503,6 +2506,59 @@ gen_trace_for_return_address (CORE_ADDR scope, struct gdbarch *gdbarch)
|
|||
return ax;
|
||||
}
|
||||
|
||||
/* Given a collection of printf-style arguments, generate code to
|
||||
evaluate the arguments and pass everything to a special
|
||||
bytecode. */
|
||||
|
||||
struct agent_expr *
|
||||
gen_printf (CORE_ADDR scope, struct gdbarch *gdbarch,
|
||||
CORE_ADDR function, LONGEST channel,
|
||||
char *format, int fmtlen,
|
||||
struct format_piece *frags,
|
||||
int nargs, struct expression **exprs)
|
||||
{
|
||||
struct expression *expr;
|
||||
struct cleanup *old_chain = 0;
|
||||
struct agent_expr *ax = new_agent_expr (gdbarch, scope);
|
||||
union exp_element *pc;
|
||||
struct axs_value value;
|
||||
int i, tem, bot, fr, flen;
|
||||
char *fmt;
|
||||
|
||||
old_chain = make_cleanup_free_agent_expr (ax);
|
||||
|
||||
/* Evaluate and push the args on the stack in reverse order,
|
||||
for simplicity of collecting them on the target side. */
|
||||
for (tem = nargs - 1; tem >= 0; --tem)
|
||||
{
|
||||
pc = exprs[tem]->elts;
|
||||
/* We're computing values, not doing side effects. */
|
||||
trace_kludge = 0;
|
||||
value.optimized_out = 0;
|
||||
gen_expr (exprs[tem], &pc, ax, &value);
|
||||
require_rvalue (ax, &value);
|
||||
}
|
||||
|
||||
/* Push function and channel. */
|
||||
ax_const_l (ax, channel);
|
||||
ax_const_l (ax, function);
|
||||
|
||||
/* Issue the printf bytecode proper. */
|
||||
ax_simple (ax, aop_printf);
|
||||
ax_simple (ax, nargs);
|
||||
ax_string (ax, format, fmtlen);
|
||||
|
||||
/* And terminate. */
|
||||
ax_simple (ax, aop_end);
|
||||
|
||||
/* We have successfully built the agent expr, so cancel the cleanup
|
||||
request. If we add more cleanups that we always want done, this
|
||||
will have to get more complicated. */
|
||||
discard_cleanups (old_chain);
|
||||
|
||||
return ax;
|
||||
}
|
||||
|
||||
static void
|
||||
agent_command (char *exp, int from_tty)
|
||||
{
|
||||
|
@ -2586,6 +2642,88 @@ agent_eval_command (char *exp, int from_tty)
|
|||
do_cleanups (old_chain);
|
||||
dont_repeat ();
|
||||
}
|
||||
/* Parse the given expression, compile it into an agent expression
|
||||
that does a printf, and display the resulting expression. */
|
||||
|
||||
static void
|
||||
maint_agent_printf_command (char *exp, int from_tty)
|
||||
{
|
||||
struct cleanup *old_chain = 0;
|
||||
struct expression *expr;
|
||||
struct expression *argvec[100];
|
||||
struct agent_expr *agent;
|
||||
struct frame_info *fi = get_current_frame (); /* need current scope */
|
||||
char *cmdrest;
|
||||
char *format_start, *format_end;
|
||||
struct format_piece *fpieces;
|
||||
int nargs;
|
||||
|
||||
/* We don't deal with overlay debugging at the moment. We need to
|
||||
think more carefully about this. If you copy this code into
|
||||
another command, change the error message; the user shouldn't
|
||||
have to know anything about agent expressions. */
|
||||
if (overlay_debugging)
|
||||
error (_("GDB can't do agent expression translation with overlays."));
|
||||
|
||||
if (exp == 0)
|
||||
error_no_arg (_("expression to translate"));
|
||||
|
||||
cmdrest = exp;
|
||||
|
||||
cmdrest = skip_spaces (cmdrest);
|
||||
|
||||
if (*cmdrest++ != '"')
|
||||
error (_("Must start with a format string."));
|
||||
|
||||
format_start = cmdrest;
|
||||
|
||||
fpieces = parse_format_string (&cmdrest);
|
||||
|
||||
old_chain = make_cleanup (free_format_pieces_cleanup, &fpieces);
|
||||
|
||||
format_end = cmdrest;
|
||||
|
||||
if (*cmdrest++ != '"')
|
||||
error (_("Bad format string, non-terminated '\"'."));
|
||||
|
||||
cmdrest = skip_spaces (cmdrest);
|
||||
|
||||
if (*cmdrest != ',' && *cmdrest != 0)
|
||||
error (_("Invalid argument syntax"));
|
||||
|
||||
if (*cmdrest == ',')
|
||||
cmdrest++;
|
||||
cmdrest = skip_spaces (cmdrest);
|
||||
|
||||
nargs = 0;
|
||||
while (*cmdrest != '\0')
|
||||
{
|
||||
char *cmd1;
|
||||
|
||||
cmd1 = cmdrest;
|
||||
expr = parse_exp_1 (&cmd1, 0, (struct block *) 0, 1);
|
||||
argvec[nargs] = expr;
|
||||
++nargs;
|
||||
cmdrest = cmd1;
|
||||
if (*cmdrest == ',')
|
||||
++cmdrest;
|
||||
/* else complain? */
|
||||
}
|
||||
|
||||
|
||||
agent = gen_printf (get_frame_pc (fi), get_current_arch (), 0, 0,
|
||||
format_start, format_end - format_start,
|
||||
fpieces, nargs, argvec);
|
||||
make_cleanup_free_agent_expr (agent);
|
||||
ax_reqs (agent);
|
||||
ax_print (gdb_stdout, agent);
|
||||
|
||||
/* It would be nice to call ax_reqs here to gather some general info
|
||||
about the expression, and then print out the result. */
|
||||
|
||||
do_cleanups (old_chain);
|
||||
dont_repeat ();
|
||||
}
|
||||
|
||||
|
||||
/* Initialization code. */
|
||||
|
@ -2603,4 +2741,9 @@ _initialize_ax_gdb (void)
|
|||
_("Translate an expression into remote "
|
||||
"agent bytecode for evaluation."),
|
||||
&maintenancelist);
|
||||
|
||||
add_cmd ("agent-printf", class_maintenance, maint_agent_printf_command,
|
||||
_("Translate an expression into remote "
|
||||
"agent bytecode for evaluation and display the bytecodes."),
|
||||
&maintenancelist);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
3
gdb/ax.h
3
gdb/ax.h
|
@ -219,6 +219,9 @@ extern void ax_reg_mask (struct agent_expr *ax, int reg);
|
|||
|
||||
/* Assemble code to operate on a trace state variable. */
|
||||
extern void ax_tsv (struct agent_expr *expr, enum agent_op op, int num);
|
||||
|
||||
/* Append a string to the bytecode stream. */
|
||||
extern void ax_string (struct agent_expr *x, char *str, int slen);
|
||||
|
||||
|
||||
/* Functions for printing out expressions, and otherwise debugging
|
||||
|
|
329
gdb/breakpoint.c
329
gdb/breakpoint.c
|
@ -70,6 +70,8 @@
|
|||
#include "ax-gdb.h"
|
||||
#include "dummy-frame.h"
|
||||
|
||||
#include "format.h"
|
||||
|
||||
/* readline include files */
|
||||
#include "readline/readline.h"
|
||||
#include "readline/history.h"
|
||||
|
@ -301,6 +303,45 @@ static struct breakpoint_ops bkpt_probe_breakpoint_ops;
|
|||
/* Dynamic printf class type. */
|
||||
static struct breakpoint_ops dprintf_breakpoint_ops;
|
||||
|
||||
/* The style in which to perform a dynamic printf. This is a user
|
||||
option because different output options have different tradeoffs;
|
||||
if GDB does the printing, there is better error handling if there
|
||||
is a problem with any of the arguments, but using an inferior
|
||||
function lets you have special-purpose printers and sending of
|
||||
output to the same place as compiled-in print functions. */
|
||||
|
||||
static const char dprintf_style_gdb[] = "gdb";
|
||||
static const char dprintf_style_call[] = "call";
|
||||
static const char dprintf_style_agent[] = "agent";
|
||||
static const char *const dprintf_style_enums[] = {
|
||||
dprintf_style_gdb,
|
||||
dprintf_style_call,
|
||||
dprintf_style_agent,
|
||||
NULL
|
||||
};
|
||||
static const char *dprintf_style = dprintf_style_gdb;
|
||||
|
||||
/* The function to use for dynamic printf if the preferred style is to
|
||||
call into the inferior. The value is simply a string that is
|
||||
copied into the command, so it can be anything that GDB can
|
||||
evaluate to a callable address, not necessarily a function name. */
|
||||
|
||||
static char *dprintf_function = "";
|
||||
|
||||
/* The channel to use for dynamic printf if the preferred style is to
|
||||
call into the inferior; if a nonempty string, it will be passed to
|
||||
the call as the first argument, with the format string as the
|
||||
second. As with the dprintf function, this can be anything that
|
||||
GDB knows how to evaluate, so in addition to common choices like
|
||||
"stderr", this could be an app-specific expression like
|
||||
"mystreams[curlogger]". */
|
||||
|
||||
static char *dprintf_channel = "";
|
||||
|
||||
/* True if dprintf commands should continue to operate even if GDB
|
||||
has disconnected. */
|
||||
static int disconnected_dprintf = 1;
|
||||
|
||||
/* A reference-counted struct command_line. This lets multiple
|
||||
breakpoints share a single command list. */
|
||||
struct counted_command_line
|
||||
|
@ -2132,6 +2173,196 @@ build_target_condition_list (struct bp_location *bl)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Parses a command described by string CMD into an agent expression
|
||||
bytecode suitable for evaluation by the bytecode interpreter.
|
||||
Return NULL if there was any error during parsing. */
|
||||
|
||||
static struct agent_expr *
|
||||
parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd)
|
||||
{
|
||||
struct cleanup *old_cleanups = 0;
|
||||
struct expression *expr, **argvec;
|
||||
struct agent_expr *aexpr = NULL;
|
||||
struct cleanup *old_chain = NULL;
|
||||
volatile struct gdb_exception ex;
|
||||
char *cmdrest;
|
||||
char *format_start, *format_end;
|
||||
struct format_piece *fpieces;
|
||||
int nargs;
|
||||
struct gdbarch *gdbarch = get_current_arch ();
|
||||
|
||||
if (!cmd)
|
||||
return NULL;
|
||||
|
||||
cmdrest = cmd;
|
||||
|
||||
if (*cmdrest == ',')
|
||||
++cmdrest;
|
||||
cmdrest = skip_spaces (cmdrest);
|
||||
|
||||
if (*cmdrest++ != '"')
|
||||
error (_("No format string following the location"));
|
||||
|
||||
format_start = cmdrest;
|
||||
|
||||
fpieces = parse_format_string (&cmdrest);
|
||||
|
||||
old_cleanups = make_cleanup (free_format_pieces_cleanup, &fpieces);
|
||||
|
||||
format_end = cmdrest;
|
||||
|
||||
if (*cmdrest++ != '"')
|
||||
error (_("Bad format string, non-terminated '\"'."));
|
||||
|
||||
cmdrest = skip_spaces (cmdrest);
|
||||
|
||||
if (!(*cmdrest == ',' || *cmdrest == '\0'))
|
||||
error (_("Invalid argument syntax"));
|
||||
|
||||
if (*cmdrest == ',')
|
||||
cmdrest++;
|
||||
cmdrest = skip_spaces (cmdrest);
|
||||
|
||||
/* For each argument, make an expression. */
|
||||
|
||||
argvec = (struct expression **) alloca (strlen (cmd)
|
||||
* sizeof (struct expression *));
|
||||
|
||||
nargs = 0;
|
||||
while (*cmdrest != '\0')
|
||||
{
|
||||
char *cmd1;
|
||||
|
||||
cmd1 = cmdrest;
|
||||
expr = parse_exp_1 (&cmd1, scope, block_for_pc (scope), 1);
|
||||
argvec[nargs++] = expr;
|
||||
cmdrest = cmd1;
|
||||
if (*cmdrest == ',')
|
||||
++cmdrest;
|
||||
}
|
||||
|
||||
/* We don't want to stop processing, so catch any errors
|
||||
that may show up. */
|
||||
TRY_CATCH (ex, RETURN_MASK_ERROR)
|
||||
{
|
||||
aexpr = gen_printf (scope, gdbarch, 0, 0,
|
||||
format_start, format_end - format_start,
|
||||
fpieces, nargs, argvec);
|
||||
}
|
||||
|
||||
if (ex.reason < 0)
|
||||
{
|
||||
/* If we got here, it means the command could not be parsed to a valid
|
||||
bytecode expression and thus can't be evaluated on the target's side.
|
||||
It's no use iterating through the other commands. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do_cleanups (old_cleanups);
|
||||
|
||||
/* We have a valid agent expression, return it. */
|
||||
return aexpr;
|
||||
}
|
||||
|
||||
/* Based on location BL, create a list of breakpoint commands to be
|
||||
passed on to the target. If we have duplicated locations with
|
||||
different commands, we will add any such to the list. */
|
||||
|
||||
static void
|
||||
build_target_command_list (struct bp_location *bl)
|
||||
{
|
||||
struct bp_location **locp = NULL, **loc2p;
|
||||
int null_command_or_parse_error = 0;
|
||||
int modified = bl->needs_update;
|
||||
struct bp_location *loc;
|
||||
|
||||
/* For now, limit to agent-style dprintf breakpoints. */
|
||||
if (bl->owner->type != bp_dprintf
|
||||
|| strcmp (dprintf_style, dprintf_style_agent) != 0)
|
||||
return;
|
||||
|
||||
if (!target_can_run_breakpoint_commands ())
|
||||
return;
|
||||
|
||||
/* Do a first pass to check for locations with no assigned
|
||||
conditions or conditions that fail to parse to a valid agent expression
|
||||
bytecode. If any of these happen, then it's no use to send conditions
|
||||
to the target since this location will always trigger and generate a
|
||||
response back to GDB. */
|
||||
ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
|
||||
{
|
||||
loc = (*loc2p);
|
||||
if (is_breakpoint (loc->owner) && loc->pspace->num == bl->pspace->num)
|
||||
{
|
||||
if (modified)
|
||||
{
|
||||
struct agent_expr *aexpr;
|
||||
|
||||
/* Re-parse the commands since something changed. In that
|
||||
case we already freed the command bytecodes (see
|
||||
force_breakpoint_reinsertion). We just
|
||||
need to parse the command to bytecodes again. */
|
||||
aexpr = parse_cmd_to_aexpr (bl->address,
|
||||
loc->owner->extra_string);
|
||||
loc->cmd_bytecode = aexpr;
|
||||
|
||||
if (!aexpr)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we have a NULL bytecode expression, it means something
|
||||
went wrong or we have a null command expression. */
|
||||
if (!loc->cmd_bytecode)
|
||||
{
|
||||
null_command_or_parse_error = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If anything failed, then we're not doing target-side commands,
|
||||
and so clean up. */
|
||||
if (null_command_or_parse_error)
|
||||
{
|
||||
ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
|
||||
{
|
||||
loc = (*loc2p);
|
||||
if (is_breakpoint (loc->owner)
|
||||
&& loc->pspace->num == bl->pspace->num)
|
||||
{
|
||||
/* Only go as far as the first NULL bytecode is
|
||||
located. */
|
||||
if (!loc->cond_bytecode)
|
||||
return;
|
||||
|
||||
free_agent_expr (loc->cond_bytecode);
|
||||
loc->cond_bytecode = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No NULL commands or failed bytecode generation. Build a command list
|
||||
for this location's address. */
|
||||
ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
|
||||
{
|
||||
loc = (*loc2p);
|
||||
if (loc->owner->extra_string
|
||||
&& is_breakpoint (loc->owner)
|
||||
&& loc->pspace->num == bl->pspace->num
|
||||
&& loc->owner->enable_state == bp_enabled
|
||||
&& loc->enabled)
|
||||
/* Add the command to the vector. This will be used later
|
||||
to send the commands to the target. */
|
||||
VEC_safe_push (agent_expr_p, bl->target_info.tcommands,
|
||||
loc->cmd_bytecode);
|
||||
}
|
||||
|
||||
bl->target_info.persist = 0;
|
||||
/* Maybe flag this location as persistent. */
|
||||
if (bl->owner->type == bp_dprintf && disconnected_dprintf)
|
||||
bl->target_info.persist = 1;
|
||||
}
|
||||
|
||||
/* Insert a low-level "breakpoint" of some type. BL is the breakpoint
|
||||
location. Any error messages are printed to TMP_ERROR_STREAM; and
|
||||
DISABLED_BREAKS, and HW_BREAKPOINT_ERROR are used to report problems.
|
||||
|
@ -2172,7 +2403,8 @@ insert_bp_location (struct bp_location *bl,
|
|||
if (is_breakpoint (bl->owner))
|
||||
{
|
||||
build_target_condition_list (bl);
|
||||
/* Reset the condition modification marker. */
|
||||
build_target_command_list (bl);
|
||||
/* Reset the modification marker. */
|
||||
bl->needs_update = 0;
|
||||
}
|
||||
|
||||
|
@ -2691,6 +2923,9 @@ remove_breakpoints_pid (int pid)
|
|||
if (bl->pspace != inf->pspace)
|
||||
continue;
|
||||
|
||||
if (bl->owner->type == bp_dprintf)
|
||||
continue;
|
||||
|
||||
if (bl->inserted)
|
||||
{
|
||||
val = remove_breakpoint (bl, mark_uninserted);
|
||||
|
@ -5827,6 +6062,15 @@ print_one_breakpoint_location (struct breakpoint *b,
|
|||
}
|
||||
}
|
||||
|
||||
if (!part_of_multiple && b->extra_string
|
||||
&& b->type == bp_dprintf && !b->commands)
|
||||
{
|
||||
annotate_field (7);
|
||||
ui_out_text (uiout, "\t(agent printf) ");
|
||||
ui_out_field_string (uiout, "printf", b->extra_string);
|
||||
ui_out_text (uiout, "\n");
|
||||
}
|
||||
|
||||
l = b->commands ? b->commands->commands : NULL;
|
||||
if (!part_of_multiple && l)
|
||||
{
|
||||
|
@ -8525,40 +8769,6 @@ bp_loc_is_permanent (struct bp_location *loc)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* The style in which to perform a dynamic printf. This is a user
|
||||
option because different output options have different tradeoffs;
|
||||
if GDB does the printing, there is better error handling if there
|
||||
is a problem with any of the arguments, but using an inferior
|
||||
function lets you have special-purpose printers and sending of
|
||||
output to the same place as compiled-in print functions. (Future
|
||||
styles may include the ability to do a target-side printf.) */
|
||||
|
||||
static const char dprintf_style_gdb[] = "gdb";
|
||||
static const char dprintf_style_call[] = "call";
|
||||
static const char *const dprintf_style_enums[] = {
|
||||
dprintf_style_gdb,
|
||||
dprintf_style_call,
|
||||
NULL
|
||||
};
|
||||
static const char *dprintf_style = dprintf_style_gdb;
|
||||
|
||||
/* The function to use for dynamic printf if the preferred style is to
|
||||
call into the inferior. The value is simply a string that is
|
||||
copied into the command, so it can be anything that GDB can
|
||||
evaluate to a callable address, not necessarily a function name. */
|
||||
|
||||
static char *dprintf_function = "";
|
||||
|
||||
/* The channel to use for dynamic printf if the preferred style is to
|
||||
call into the inferior; if a nonempty string, it will be passed to
|
||||
the call as the first argument, with the format string as the
|
||||
second. As with the dprintf function, this can be anything that
|
||||
GDB knows how to evaluate, so in addition to common choices like
|
||||
"stderr", this could be an app-specific expression like
|
||||
"mystreams[curlogger]". */
|
||||
|
||||
static char *dprintf_channel = "";
|
||||
|
||||
/* Build a command list for the dprintf corresponding to the current
|
||||
settings of the dprintf style options. */
|
||||
|
||||
|
@ -8582,9 +8792,9 @@ update_dprintf_command_list (struct breakpoint *b)
|
|||
if (*dprintf_args != '"')
|
||||
error (_("Bad format string, missing '\"'."));
|
||||
|
||||
if (strcmp (dprintf_style, "gdb") == 0)
|
||||
if (strcmp (dprintf_style, dprintf_style_gdb) == 0)
|
||||
printf_line = xstrprintf ("printf %s", dprintf_args);
|
||||
else if (strcmp (dprintf_style, "call") == 0)
|
||||
else if (strcmp (dprintf_style, dprintf_style_call) == 0)
|
||||
{
|
||||
if (!dprintf_function)
|
||||
error (_("No function supplied for dprintf call"));
|
||||
|
@ -8599,6 +8809,16 @@ update_dprintf_command_list (struct breakpoint *b)
|
|||
dprintf_function,
|
||||
dprintf_args);
|
||||
}
|
||||
else if (strcmp (dprintf_style, dprintf_style_agent) == 0)
|
||||
{
|
||||
if (target_can_run_breakpoint_commands ())
|
||||
printf_line = xstrprintf ("agent-printf %s", dprintf_args);
|
||||
else
|
||||
{
|
||||
warning (_("Target cannot run dprintf commands, falling back to GDB printf"));
|
||||
printf_line = xstrprintf ("printf %s", dprintf_args);
|
||||
}
|
||||
}
|
||||
else
|
||||
internal_error (__FILE__, __LINE__,
|
||||
_("Invalid dprintf style."));
|
||||
|
@ -8608,12 +8828,15 @@ update_dprintf_command_list (struct breakpoint *b)
|
|||
{
|
||||
struct command_line *printf_cmd_line, *cont_cmd_line = NULL;
|
||||
|
||||
cont_cmd_line = xmalloc (sizeof (struct command_line));
|
||||
cont_cmd_line->control_type = simple_control;
|
||||
cont_cmd_line->body_count = 0;
|
||||
cont_cmd_line->body_list = NULL;
|
||||
cont_cmd_line->next = NULL;
|
||||
cont_cmd_line->line = xstrdup ("continue");
|
||||
if (strcmp (dprintf_style, dprintf_style_agent) != 0)
|
||||
{
|
||||
cont_cmd_line = xmalloc (sizeof (struct command_line));
|
||||
cont_cmd_line->control_type = simple_control;
|
||||
cont_cmd_line->body_count = 0;
|
||||
cont_cmd_line->body_list = NULL;
|
||||
cont_cmd_line->next = NULL;
|
||||
cont_cmd_line->line = xstrdup ("continue");
|
||||
}
|
||||
|
||||
printf_cmd_line = xmalloc (sizeof (struct command_line));
|
||||
printf_cmd_line->control_type = simple_control;
|
||||
|
@ -9570,6 +9793,12 @@ dprintf_command (char *arg, int from_tty)
|
|||
0);
|
||||
}
|
||||
|
||||
static void
|
||||
agent_printf_command (char *arg, int from_tty)
|
||||
{
|
||||
error (_("May only run agent-printf on the target"));
|
||||
}
|
||||
|
||||
/* Implement the "breakpoint_hit" breakpoint_ops method for
|
||||
ranged breakpoints. */
|
||||
|
||||
|
@ -16244,6 +16473,20 @@ Show the channel to use for dynamic printf"), NULL,
|
|||
update_dprintf_commands, NULL,
|
||||
&setlist, &showlist);
|
||||
|
||||
add_setshow_boolean_cmd ("disconnected-dprintf", no_class,
|
||||
&disconnected_dprintf, _("\
|
||||
Set whether dprintf continues after GDB disconnects."), _("\
|
||||
Show whether dprintf continues after GDB disconnects."), _("\
|
||||
Use this to let dprintf commands continue to hit and produce output\n\
|
||||
even if GDB disconnects or detaches from the target."),
|
||||
NULL,
|
||||
NULL,
|
||||
&setlist, &showlist);
|
||||
|
||||
add_com ("agent-printf", class_vars, agent_printf_command, _("\
|
||||
agent-printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
|
||||
(target agent only) This is useful for formatted output in user-defined commands."));
|
||||
|
||||
automatic_hardware_breakpoints = 1;
|
||||
|
||||
observer_attach_about_to_proceed (breakpoint_about_to_proceed);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
400
gdb/common/format.c
Normal 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
63
gdb/common/format.h
Normal 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 *);
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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::
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
341
gdb/printcmd.c
341
gdb/printcmd.c
|
@ -49,18 +49,12 @@
|
|||
#include "charset.h"
|
||||
#include "arch-utils.h"
|
||||
#include "cli/cli-utils.h"
|
||||
#include "format.h"
|
||||
|
||||
#ifdef TUI
|
||||
#include "tui/tui.h" /* For tui_active et al. */
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG)
|
||||
# define USE_PRINTF_I64 1
|
||||
# define PRINTF_HAS_LONG_LONG
|
||||
#else
|
||||
# define USE_PRINTF_I64 0
|
||||
#endif
|
||||
|
||||
struct format_data
|
||||
{
|
||||
int count;
|
||||
|
@ -2001,13 +1995,9 @@ print_variable_and_value (const char *name, struct symbol *var,
|
|||
static void
|
||||
ui_printf (char *arg, struct ui_file *stream)
|
||||
{
|
||||
char *f = NULL;
|
||||
struct format_piece *fpieces;
|
||||
char *s = arg;
|
||||
char *string = NULL;
|
||||
struct value **val_args;
|
||||
char *substrings;
|
||||
char *current_substring;
|
||||
int nargs = 0;
|
||||
int allocated_args = 20;
|
||||
struct cleanup *old_cleanups;
|
||||
|
||||
|
@ -2023,64 +2013,13 @@ ui_printf (char *arg, struct ui_file *stream)
|
|||
if (*s++ != '"')
|
||||
error (_("Bad format string, missing '\"'."));
|
||||
|
||||
/* Parse the format-control string and copy it into the string STRING,
|
||||
processing some kinds of escape sequence. */
|
||||
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);
|
||||
}
|
||||
|
|
69
gdb/remote.c
69
gdb/remote.c
|
@ -242,6 +242,8 @@ static void remote_console_output (char *msg);
|
|||
|
||||
static int remote_supports_cond_breakpoints (void);
|
||||
|
||||
static int remote_can_run_breakpoint_commands (void);
|
||||
|
||||
/* The non-stop remote protocol provisions for one pending stop reply.
|
||||
This is where we keep it until it is acknowledged. */
|
||||
|
||||
|
@ -323,6 +325,10 @@ struct remote_state
|
|||
conditions. */
|
||||
int cond_breakpoints;
|
||||
|
||||
/* True if the stub reports support for target-side breakpoint
|
||||
commands. */
|
||||
int breakpoint_commands;
|
||||
|
||||
/* True if the stub reports support for fast tracepoints. */
|
||||
int fast_tracepoints;
|
||||
|
||||
|
@ -1274,6 +1280,7 @@ enum {
|
|||
PACKET_qAttached,
|
||||
PACKET_ConditionalTracepoints,
|
||||
PACKET_ConditionalBreakpoints,
|
||||
PACKET_BreakpointCommands,
|
||||
PACKET_FastTracepoints,
|
||||
PACKET_StaticTracepoints,
|
||||
PACKET_InstallInTrace,
|
||||
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
10
gdb/target.h
10
gdb/target.h
|
@ -672,6 +672,10 @@ struct target_ops
|
|||
end? */
|
||||
int (*to_supports_evaluation_of_breakpoint_conditions) (void);
|
||||
|
||||
/* Does this target support evaluation of breakpoint commands on its
|
||||
end? */
|
||||
int (*to_can_run_breakpoint_commands) (void);
|
||||
|
||||
/* Determine current architecture of thread PTID.
|
||||
|
||||
The target is supposed to determine the architecture of the code where
|
||||
|
@ -997,6 +1001,12 @@ int target_supports_disable_randomization (void);
|
|||
#define target_supports_evaluation_of_breakpoint_conditions() \
|
||||
(*current_target.to_supports_evaluation_of_breakpoint_conditions) ()
|
||||
|
||||
/* Returns true if this target can handle breakpoint commands
|
||||
on its end. */
|
||||
|
||||
#define target_can_run_breakpoint_commands() \
|
||||
(*current_target.to_can_run_breakpoint_commands) ()
|
||||
|
||||
/* Invalidate all target dcaches. */
|
||||
extern void target_dcache_invalidate (void);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue