aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver
diff options
context:
space:
mode:
authorStan Shebs <shebs@codesourcery.com>2012-07-02 15:29:39 +0000
committerStan Shebs <shebs@codesourcery.com>2012-07-02 15:29:39 +0000
commitd3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2 (patch)
tree782811ca1df7a4f775823d1918d0b571fdac9b5a /gdb/gdbserver
parenta47edf2745dd6414d635e4b372d416035c7b8c12 (diff)
downloadgdb-d3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2.zip
gdb-d3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2.tar.gz
gdb-d3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2.tar.bz2
Add target-side support for dynamic printf.
* NEWS: Mention the additional style. * breakpoint.h (struct bp_target_info): New fields tcommands, persist. (struct bp_location): New field cmd_bytecode. * breakpoint.c: Include format.h. (disconnected_dprintf): New global. (parse_cmd_to_aexpr): New function. (build_target_command_list): New function. (insert_bp_location): Call it. (remove_breakpoints_pid): Skip dprintf breakpoints. (print_one_breakpoint_location): Ditto. (dprintf_style_agent): New global. (dprintf_style_enums): Add dprintf_style_agent. (update_dprintf_command_list): Add agent case. (agent_printf_command): New function. (_initialize_breakpoint): Add new commands. * common/ax.def (printf): New bytecode. * ax.h (ax_string): Declare. * ax-gdb.h (gen_printf): Declare. * ax-gdb.c: Include cli-utils.h, format.h. (gen_printf): New function. (maint_agent_print_command): New function. (_initialize_ax_gdb): Add maint agent-printf command. * ax-general.c (ax_string): New function. (ax_print): Add printf disassembly. * Makefile.in (SFILES): Add format.c (COMMON_OBS): Add format.o. * common/format.h: New file. * common/format.c: New file. * printcmd.c: Include format.h. (ui_printf): Call parse_format_string. * remote.c (remote_state): New field breakpoint_commands. (PACKET_BreakpointCommands): New enum. (remote_breakpoint_commands_feature): New function. (remote_protocol_features): Add new BreakpointCommands entry. (remote_can_run_breakpoint_commands): New function. (remote_add_target_side_commands): New function. (remote_insert_breakpoint): Call it. (remote_insert_hw_breakpoint): Ditto. (_initialize_remote): Add new packet configuration for target-side breakpoint commands. * target.h (struct target_ops): New field to_can_run_breakpoint_commands. (target_can_run_breakpoint_commands): New macro. * target.c (update_current_target): Handle to_can_run_breakpoint_commands. [gdbserver] * Makefile.in (WARN_CFLAGS_NO_FORMAT): Define. (ax.o): Add it to build rule. (ax-ipa.o): Ditto. (OBS): Add format.o. (IPA_OBS): Add format.o. * server.c (handle_query): Claim support for breakpoint commands. (process_point_options): Add command case. (process_serial_event): Leave running if there are printfs in effect. * mem-break.h (any_persistent_commands): Declare. (add_breakpoint_commands): Declare. (gdb_no_commands_at_breakpoint): Declare. (run_breakpoint_commands): Declare. * mem-break.c (struct point_command_list): New struct. (struct breakpoint): New field command_list. (any_persistent_commands): New function. (add_commands_to_breakpoint): New function. (add_breakpoint_commands): New function. (gdb_no_commands_at_breakpoint): New function. (run_breakpoint_commands): New function. * linux-low.c (linux_wait_1): Test for and run breakpoint commands locally. * ax.c: Include format.h. (ax_printf): New function. (gdb_eval_agent_expr): Add printf opcode. [doc] * gdb.texinfo (Dynamic Printf): Mention agent style and disconnected dprintf. (Maintenance Commands): Describe maint agent-printf. (General Query Packets): Mention BreakpointCommands feature. (Packets): Document commands extension to Z0 packet. * agentexpr.texi (Bytecode Descriptions): Document printf bytecode. [testsuite] * gdb.base/dprintf.exp: Add agent style tests.
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r--gdb/gdbserver/ChangeLog28
-rw-r--r--gdb/gdbserver/Makefile.in15
-rw-r--r--gdb/gdbserver/ax.c155
-rw-r--r--gdb/gdbserver/linux-low.c8
-rw-r--r--gdb/gdbserver/mem-break.c125
-rw-r--r--gdb/gdbserver/mem-break.h8
-rw-r--r--gdb/gdbserver/server.c53
7 files changed, 370 insertions, 22 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 683aade..02a2398 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,31 @@
+2012-07-02 Stan Shebs <stan@codesourcery.com>
+
+ * Makefile.in (WARN_CFLAGS_NO_FORMAT): Define.
+ (ax.o): Add it to build rule.
+ (ax-ipa.o): Ditto.
+ (OBS): Add format.o.
+ (IPA_OBS): Add format.o.
+ * server.c (handle_query): Claim support for breakpoint commands.
+ (process_point_options): Add command case.
+ (process_serial_event): Leave running if there are printfs in
+ effect.
+ * mem-break.h (any_persistent_commands): Declare.
+ (add_breakpoint_commands): Declare.
+ (gdb_no_commands_at_breakpoint): Declare.
+ (run_breakpoint_commands): Declare.
+ * mem-break.c (struct point_command_list): New struct.
+ (struct breakpoint): New field command_list.
+ (any_persistent_commands): New function.
+ (add_commands_to_breakpoint): New function.
+ (add_breakpoint_commands): New function.
+ (gdb_no_commands_at_breakpoint): New function.
+ (run_breakpoint_commands): New function.
+ * linux-low.c (linux_wait_1): Test for and run breakpoint commands
+ locally.
+ * ax.c: Include format.h.
+ (ax_printf): New function.
+ (gdb_eval_agent_expr): Add printf opcode.
+
2012-06-13 Yao Qi <yao@codesourcery.com>
* server.c (start_inferior): Remove duplicated writes to fields
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 3c5c041..446ea04 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -100,6 +100,9 @@ GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS}
WARN_CFLAGS = @WARN_CFLAGS@
WERROR_CFLAGS = @WERROR_CFLAGS@
+WARN_CFLAGS_NO_FORMAT = `echo " $(WARN_CFLAGS) " \
+ | sed "s/ -Wformat-nonliteral / -Wno-format-nonliteral /g"`
+
# CFLAGS is specifically reserved for setting from the command line
# when running make. I.E. "make CFLAGS=-Wmissing-prototypes".
CFLAGS = @CFLAGS@
@@ -152,7 +155,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPARAM} ${POSSLIBS}
OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o target.o \
utils.o version.o vec.o \
mem-break.o hostio.o event-loop.o tracepoint.o \
- xml-utils.o common-utils.o ptid.o buffer.o \
+ xml-utils.o common-utils.o ptid.o buffer.o format.o \
dll.o \
$(XML_BUILTIN) \
$(DEPFILES) $(LIBOBJS)
@@ -273,7 +276,7 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS)
${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \
$(XM_CLIBS)
-IPA_OBJS=ax-ipa.o tracepoint-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
+IPA_OBJS=ax-ipa.o tracepoint-ipa.o format-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
IPA_LIB=libinproctrace.so
@@ -447,11 +450,13 @@ IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_CFLAGS) $(UST_CFLAGS) \
# In-process agent object rules
ax-ipa.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
- $(CC) -c $(IPAGENT_CFLAGS) $< -o ax-ipa.o
+ $(CC) -c $(IPAGENT_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $< -o ax-ipa.o
tracepoint-ipa.o: tracepoint.c $(server_h) ${ax_h}
$(CC) -c $(IPAGENT_CFLAGS) $< -o tracepoint-ipa.o
utils-ipa.o: utils.c $(server_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o utils-ipa.o
+format-ipa.o: ../common/format.c $(server_h) ${ax_h}
+ $(CC) -c $(IPAGENT_CFLAGS) $< -o format-ipa.o
common-utils-ipa.o: ../common/common-utils.c $(server_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o common-utils-ipa.o
remote-utils-ipa.o: remote-utils.c $(server_h)
@@ -468,6 +473,7 @@ amd64-linux-ipa.o : amd64-linux.c $(regdef_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o amd64-linux-ipa.o
ax.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
+ $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $<
event-loop.o: event-loop.c $(server_h)
hostio.o: hostio.c $(server_h)
hostio-errno.o: hostio-errno.c $(server_h)
@@ -512,6 +518,9 @@ ptid.o: ../common/ptid.c $(ptid_h)
buffer.o: ../common/buffer.c $(server_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
+format.o: ../common/format.c $(server_h)
+ $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
+
agent.o: ../common/agent.c $(server_h) $(agent_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
diff --git a/gdb/gdbserver/ax.c b/gdb/gdbserver/ax.c
index 4075c26..70e9322 100644
--- a/gdb/gdbserver/ax.c
+++ b/gdb/gdbserver/ax.c
@@ -18,6 +18,7 @@
#include "server.h"
#include "ax.h"
+#include "format.h"
static void ax_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2);
@@ -789,6 +790,123 @@ compile_bytecodes (struct agent_expr *aexpr)
#endif
+/* Make printf-type calls using arguments supplied from the host. We
+ need to parse the format string ourselves, and call the formatting
+ function with one argument at a time, partly because there is no
+ safe portable way to construct a varargs call, and partly to serve
+ as a security barrier against bad format strings that might get
+ in. */
+
+static void
+ax_printf (CORE_ADDR fn, CORE_ADDR chan, char *format,
+ int nargs, ULONGEST *args)
+{
+ char *f = format;
+ struct format_piece *fpieces;
+ int i, fp;
+ char *current_substring;
+ int nargs_wanted;
+
+ ax_debug ("Printf of \"%s\" with %d args", format, nargs);
+
+ fpieces = parse_format_string (&f);
+
+ nargs_wanted = 0;
+ for (fp = 0; fpieces[fp].string != NULL; fp++)
+ if (fpieces[fp].argclass != literal_piece)
+ ++nargs_wanted;
+
+ if (nargs != nargs_wanted)
+ error (_("Wrong number of arguments for specified format-string"));
+
+ i = 0;
+ for (fp = 0; fpieces[fp].string != NULL; fp++)
+ {
+ current_substring = fpieces[fp].string;
+ ax_debug ("current substring is '%s', class is %d",
+ current_substring, fpieces[fp].argclass);
+ switch (fpieces[fp].argclass)
+ {
+ case string_arg:
+ {
+ gdb_byte *str;
+ CORE_ADDR tem;
+ int j;
+
+ tem = args[i];
+
+ /* This is a %s argument. Find the length of the string. */
+ for (j = 0;; j++)
+ {
+ gdb_byte c;
+
+ read_inferior_memory (tem + j, &c, 1);
+ if (c == 0)
+ break;
+ }
+
+ /* Copy the string contents into a string inside GDB. */
+ str = (gdb_byte *) alloca (j + 1);
+ if (j != 0)
+ read_inferior_memory (tem, str, j);
+ str[j] = 0;
+
+ printf (current_substring, (char *) str);
+ }
+ break;
+
+ case long_long_arg:
+#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG)
+ {
+ long long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+#else
+ error (_("long long not supported in agent printf"));
+#endif
+ case int_arg:
+ {
+ int val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case long_arg:
+ {
+ long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case literal_piece:
+ /* Print a portion of the format string that has no
+ directives. Note that this will not include any
+ ordinary %-specs, but it might include "%%". That is
+ why we use printf_filtered and not puts_filtered here.
+ Also, we pass a dummy argument because some platforms
+ have modified GCC to include -Wformat-security by
+ default, which will warn here if there is no
+ argument. */
+ printf (current_substring, 0);
+ break;
+
+ default:
+ error (_("Format directive in '%s' not supported in agent printf"),
+ current_substring);
+ }
+
+ /* Maybe advance to the next argument. */
+ if (fpieces[fp].argclass != literal_piece)
+ ++i;
+ }
+
+ free_format_pieces (fpieces);
+}
+
/* The agent expression evaluator, as specified by the GDB docs. It
returns 0 if everything went OK, and a nonzero error code
otherwise. */
@@ -1152,6 +1270,43 @@ gdb_eval_agent_expr (struct regcache *regcache,
top = stack[sp];
break;
+ case gdb_agent_op_printf:
+ {
+ int nargs, slen, i;
+ CORE_ADDR fn = 0, chan = 0;
+ /* Can't have more args than the entire size of the stack. */
+ ULONGEST args[STACK_MAX];
+ char *format;
+
+ nargs = aexpr->bytes[pc++];
+ slen = aexpr->bytes[pc++];
+ slen = (slen << 8) + aexpr->bytes[pc++];
+ format = (char *) &(aexpr->bytes[pc]);
+ pc += slen;
+ /* Pop function and channel. */
+ fn = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ chan = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ /* Pop arguments into a dedicated array. */
+ for (i = 0; i < nargs; ++i)
+ {
+ args[i] = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ }
+
+ /* A bad format string means something is very wrong; give
+ up immediately. */
+ if (format[slen - 1] != '\0')
+ error (_("Unterminated format string in printf bytecode"));
+
+ ax_printf (fn, chan, format, nargs, args);
+ }
+ break;
+
/* GDB never (currently) generates any of these ops. */
case gdb_agent_op_float:
case gdb_agent_op_ref_float:
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3d116fd..48134c3 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2618,7 +2618,10 @@ Check if we're already there.\n",
|| (!step_over_finished
&& !bp_explains_trap && !trace_event)
|| (gdb_breakpoint_here (event_child->stop_pc)
- && gdb_condition_true_at_breakpoint (event_child->stop_pc)));
+ && gdb_condition_true_at_breakpoint (event_child->stop_pc)
+ && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
+
+ run_breakpoint_commands (event_child->stop_pc);
/* We found no reason GDB would want us to stop. We either hit one
of our own breakpoints, or finished an internal step GDB
@@ -3499,7 +3502,8 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy)
though. If the condition is being evaluated on the target's side
and it evaluate to false, step over this breakpoint as well. */
if (gdb_breakpoint_here (pc)
- && gdb_condition_true_at_breakpoint (pc))
+ && gdb_condition_true_at_breakpoint (pc)
+ && gdb_no_commands_at_breakpoint (pc))
{
if (debug_threads)
fprintf (stderr,
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 6b6b25c..aec09ba 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -97,6 +97,20 @@ struct point_cond_list
struct point_cond_list *next;
};
+struct point_command_list
+{
+ /* Pointer to the agent expression that is the breakpoint's
+ commands. */
+ struct agent_expr *cmd;
+
+ /* Flag that is true if this command should run even while GDB is
+ disconnected. */
+ int persistence;
+
+ /* Pointer to the next command. */
+ struct point_command_list *next;
+};
+
/* A high level (in gdbserver's perspective) breakpoint. */
struct breakpoint
{
@@ -111,6 +125,9 @@ struct breakpoint
target's side. */
struct point_cond_list *cond_list;
+ /* Point to the list of commands to run when this is hit. */
+ struct point_command_list *command_list;
+
/* Link to this breakpoint's raw breakpoint. This is always
non-NULL. */
struct raw_breakpoint *raw;
@@ -121,6 +138,23 @@ struct breakpoint
int (*handler) (CORE_ADDR);
};
+int
+any_persistent_commands ()
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+ struct point_command_list *cl;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ for (cl = bp->command_list; cl != NULL; cl = cl->next)
+ if (cl->persistence)
+ return 1;
+ }
+
+ return 0;
+}
+
static struct raw_breakpoint *
find_raw_breakpoint_at (CORE_ADDR where)
{
@@ -835,6 +869,97 @@ gdb_condition_true_at_breakpoint (CORE_ADDR where)
return (value != 0);
}
+/* Add commands COMMANDS to GDBserver's breakpoint BP. */
+
+void
+add_commands_to_breakpoint (struct breakpoint *bp,
+ struct agent_expr *commands, int persist)
+{
+ struct point_command_list *new_cmd;
+
+ /* Create new command. */
+ new_cmd = xcalloc (1, sizeof (*new_cmd));
+ new_cmd->cmd = commands;
+ new_cmd->persistence = persist;
+
+ /* Add commands to the list. */
+ new_cmd->next = bp->command_list;
+ bp->command_list = new_cmd;
+}
+
+/* Add a target-side command COMMAND to the breakpoint at ADDR. */
+
+int
+add_breakpoint_commands (CORE_ADDR addr, char **command, int persist)
+{
+ struct breakpoint *bp = find_gdb_breakpoint_at (addr);
+ char *actparm = *command;
+ struct agent_expr *cmd;
+
+ if (bp == NULL)
+ return 1;
+
+ if (command == NULL)
+ return 1;
+
+ cmd = gdb_parse_agent_expr (&actparm);
+
+ if (cmd == NULL)
+ {
+ fprintf (stderr, "Command evaluation failed. "
+ "Disabling.\n");
+ return 0;
+ }
+
+ add_commands_to_breakpoint (bp, cmd, persist);
+
+ *command = actparm;
+
+ return 0;
+}
+
+/* Return true if there are no commands to run at this location,
+ which likely means we want to report back to GDB. */
+int
+gdb_no_commands_at_breakpoint (CORE_ADDR where)
+{
+ struct breakpoint *bp = find_gdb_breakpoint_at (where);
+
+ if (bp == NULL)
+ return 0;
+
+ if (debug_threads)
+ fprintf (stderr, "at 0x%s, bp command_list is 0x%x\n",
+ paddress (where), (int) bp->command_list);
+ return (bp->command_list == NULL);
+}
+
+void
+run_breakpoint_commands (CORE_ADDR where)
+{
+ /* Fetch registers for the current inferior. */
+ struct breakpoint *bp = find_gdb_breakpoint_at (where);
+ ULONGEST value = 0;
+ struct point_command_list *cl;
+ int err = 0;
+
+ struct regcache *regcache = get_thread_regcache (current_inferior, 1);
+
+ if (bp == NULL)
+ return;
+
+ for (cl = bp->command_list;
+ cl && !value && !err; cl = cl->next)
+ {
+ /* Run the command. */
+ err = gdb_eval_agent_expr (regcache, NULL, cl->cmd, &value);
+
+ /* If one command has a problem, stop digging the hole deeper. */
+ if (err)
+ break;
+ }
+}
+
/* Return 1 if there is a breakpoint inserted in address WHERE
and if its condition, if it exists, is true. */
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index 95b7f9d..bb2aa03 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -52,11 +52,19 @@ void clear_gdb_breakpoint_conditions (CORE_ADDR addr);
int add_breakpoint_condition (CORE_ADDR addr, char **condition);
+int add_breakpoint_commands (CORE_ADDR addr, char **commands, int persist);
+
+int any_persistent_commands (void);
+
/* Evaluation condition (if any) at breakpoint BP. Return 1 if
true and 0 otherwise. */
int gdb_condition_true_at_breakpoint (CORE_ADDR where);
+int gdb_no_commands_at_breakpoint (CORE_ADDR where);
+
+void run_breakpoint_commands (CORE_ADDR where);
+
/* Returns TRUE if there's a GDB breakpoint set at ADDR. */
int gdb_breakpoint_here (CORE_ADDR where);
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 69b54d9..963d575 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1685,8 +1685,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
strcat (own_buf, ";tracenz+");
}
- /* Support target-side breakpoint conditions. */
+ /* Support target-side breakpoint conditions and commands. */
strcat (own_buf, ";ConditionalBreakpoints+");
+ strcat (own_buf, ";BreakpointCommands+");
if (target_supports_agent ())
strcat (own_buf, ";QAgent+");
@@ -2907,6 +2908,7 @@ static void
process_point_options (CORE_ADDR point_addr, char **packet)
{
char *dataptr = *packet;
+ int persist;
/* Check if data has the correct format. */
if (*dataptr != ';')
@@ -2916,22 +2918,33 @@ process_point_options (CORE_ADDR point_addr, char **packet)
while (*dataptr)
{
- switch (*dataptr)
+ if (*dataptr == ';')
+ ++dataptr;
+
+ if (*dataptr == 'X')
{
- case 'X':
- /* Conditional expression. */
- if (remote_debug)
- fprintf (stderr, "Found breakpoint condition.\n");
- add_breakpoint_condition (point_addr, &dataptr);
- break;
- default:
- /* Unrecognized token, just skip it. */
- fprintf (stderr, "Unknown token %c, ignoring.\n",
- *dataptr);
+ /* Conditional expression. */
+ fprintf (stderr, "Found breakpoint condition.\n");
+ add_breakpoint_condition (point_addr, &dataptr);
+ }
+ else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0)
+ {
+ dataptr += strlen ("cmds:");
+ if (debug_threads)
+ fprintf (stderr, "Found breakpoint commands %s.\n", dataptr);
+ persist = (*dataptr == '1');
+ dataptr += 2;
+ add_breakpoint_commands (point_addr, &dataptr, persist);
+ }
+ else
+ {
+ /* Unrecognized token, just skip it. */
+ fprintf (stderr, "Unknown token %c, ignoring.\n",
+ *dataptr);
}
/* Skip tokens until we find one that we recognize. */
- while (*dataptr && *dataptr != 'X' && *dataptr != ';')
+ while (*dataptr && *dataptr != ';')
dataptr++;
}
*packet = dataptr;
@@ -2997,7 +3010,7 @@ process_serial_event (void)
pid =
ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id);
- if (tracing && disconnected_tracing)
+ if ((tracing && disconnected_tracing) || any_persistent_commands ())
{
struct thread_resume resume_info;
struct process_info *process = find_process_pid (pid);
@@ -3008,9 +3021,15 @@ process_serial_event (void)
break;
}
- fprintf (stderr,
- "Disconnected tracing in effect, "
- "leaving gdbserver attached to the process\n");
+ if (tracing && disconnected_tracing)
+ fprintf (stderr,
+ "Disconnected tracing in effect, "
+ "leaving gdbserver attached to the process\n");
+
+ if (any_persistent_commands ())
+ fprintf (stderr,
+ "Persistent commands are present, "
+ "leaving gdbserver attached to the process\n");
/* Make sure we're in non-stop/async mode, so we we can both
wait for an async socket accept, and handle async target