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