diff options
author | Stan Shebs <shebs@codesourcery.com> | 2012-07-02 15:29:39 +0000 |
---|---|---|
committer | Stan Shebs <shebs@codesourcery.com> | 2012-07-02 15:29:39 +0000 |
commit | d3ce09f5bf7a7e8f97c3f1c9888e886ee267c2f2 (patch) | |
tree | 782811ca1df7a4f775823d1918d0b571fdac9b5a /gdb/gdbserver | |
parent | a47edf2745dd6414d635e4b372d416035c7b8c12 (diff) | |
download | gdb-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/ChangeLog | 28 | ||||
-rw-r--r-- | gdb/gdbserver/Makefile.in | 15 | ||||
-rw-r--r-- | gdb/gdbserver/ax.c | 155 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 8 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.c | 125 | ||||
-rw-r--r-- | gdb/gdbserver/mem-break.h | 8 | ||||
-rw-r--r-- | gdb/gdbserver/server.c | 53 |
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 |