+/* Perform the real interruption of the target execution, in response
+ to a ^C. */
+static void
+async_remote_interrupt (gdb_client_data arg)
+{
+ if (remote_debug)
+ fprintf_unfiltered (gdb_stdlog, "remote_interrupt called\n");
+
+ target_stop (inferior_ptid);
+}
+
+/* Perform interrupt, if the first attempt did not succeed. Just give
+ up on the target alltogether. */
+void
+async_remote_interrupt_twice (gdb_client_data arg)
+{
+ if (remote_debug)
+ fprintf_unfiltered (gdb_stdlog, "remote_interrupt_twice called\n");
+
+ interrupt_query ();
+}
+
+/* Reinstall the usual SIGINT handlers, after the target has
+ stopped. */
+static void
+cleanup_sigint_signal_handler (void *dummy)
+{
+ signal (SIGINT, handle_sigint);
+}
+
+/* Send ^C to target to halt it. Target will respond, and send us a
+ packet. */
+static void (*ofunc) (int);
+
+/* The command line interface's stop routine. This function is installed
+ as a signal handler for SIGINT. The first time a user requests a
+ stop, we call remote_stop to send a break or ^C. If there is no
+ response from the target (it didn't stop when the user requested it),
+ we ask the user if he'd like to detach from the target. */
+static void
+remote_interrupt (int signo)
+{
+ /* If this doesn't work, try more severe steps. */
+ signal (signo, remote_interrupt_twice);
+
+ gdb_call_async_signal_handler (sigint_remote_token, 1);
+}
+
+/* The user typed ^C twice. */
+
+static void
+remote_interrupt_twice (int signo)
+{
+ signal (signo, ofunc);
+ gdb_call_async_signal_handler (sigint_remote_twice_token, 1);
+ signal (signo, remote_interrupt);
+}
+
+/* Non-stop version of target_stop. Uses `vCont;t' to stop a remote
+ thread, all threads of a remote process, or all threads of all
+ processes. */
+
+static void
+remote_stop_ns (ptid_t ptid)
+{
+ struct remote_state *rs = get_remote_state ();
+ char *p = rs->buf;
+ char *endp = rs->buf + get_remote_packet_size ();
+ struct stop_reply *reply, *next;
+
+ if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN)
+ remote_vcont_probe (rs);
+
+ if (!rs->support_vCont_t)
+ error (_("Remote server does not support stopping threads"));
+
+ if (ptid_equal (ptid, minus_one_ptid)
+ || (!remote_multi_process_p (rs) && ptid_is_pid (ptid)))
+ p += xsnprintf (p, endp - p, "vCont;t");
+ else
+ {
+ ptid_t nptid;
+
+ p += xsnprintf (p, endp - p, "vCont;t:");
+
+ if (ptid_is_pid (ptid))
+ /* All (-1) threads of process. */
+ nptid = ptid_build (ptid_get_pid (ptid), 0, -1);
+ else
+ {
+ /* Small optimization: if we already have a stop reply for
+ this thread, no use in telling the stub we want this
+ stopped. */
+ if (peek_stop_reply (ptid))
+ return;
+
+ nptid = ptid;
+ }
+
+ p = write_ptid (p, endp, nptid);
+ }
+
+ /* In non-stop, we get an immediate OK reply. The stop reply will
+ come in asynchronously by notification. */
+ putpkt (rs->buf);
+ getpkt (&rs->buf, &rs->buf_size, 0);
+ if (strcmp (rs->buf, "OK") != 0)
+ error (_("Stopping %s failed: %s"), target_pid_to_str (ptid), rs->buf);
+}
+
+/* All-stop version of target_stop. Sends a break or a ^C to stop the
+ remote target. It is undefined which thread of which process
+ reports the stop. */
+
+static void
+remote_stop_as (ptid_t ptid)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ /* If the inferior is stopped already, but the core didn't know
+ about it yet, just ignore the request. The cached wait status
+ will be collected in remote_wait. */
+ if (rs->cached_wait_status)
+ return;
+
+ /* Send a break or a ^C, depending on user preference. */
+
+ if (remote_break)
+ serial_send_break (remote_desc);
+ else
+ serial_write (remote_desc, "\003", 1);
+}
+
+/* This is the generic stop called via the target vector. When a target
+ interrupt is requested, either by the command line or the GUI, we
+ will eventually end up here. */
+
+static void
+remote_stop (ptid_t ptid)
+{
+ if (remote_debug)
+ fprintf_unfiltered (gdb_stdlog, "remote_stop called\n");
+
+ if (non_stop)
+ remote_stop_ns (ptid);
+ else
+ remote_stop_as (ptid);
+}
+
+/* Ask the user what to do when an interrupt is received. */
+
+static void
+interrupt_query (void)
+{
+ target_terminal_ours ();
+
+ if (target_can_async_p ())
+ {
+ signal (SIGINT, handle_sigint);
+ deprecated_throw_reason (RETURN_QUIT);
+ }
+ else
+ {
+ if (query (_("Interrupted while waiting for the program.\n\
+Give up (and stop debugging it)? ")))
+ {
+ pop_target ();
+ deprecated_throw_reason (RETURN_QUIT);
+ }
+ }
+
+ target_terminal_inferior ();
+}
+
+/* Enable/disable target terminal ownership. Most targets can use
+ terminal groups to control terminal ownership. Remote targets are
+ different in that explicit transfer of ownership to/from GDB/target
+ is required. */
+
+static void
+remote_terminal_inferior (void)
+{
+ if (!target_async_permitted)
+ /* Nothing to do. */
+ return;
+
+ /* FIXME: cagney/1999-09-27: Shouldn't need to test for
+ sync_execution here. This function should only be called when
+ GDB is resuming the inferior in the forground. A background
+ resume (``run&'') should leave GDB in control of the terminal and
+ consequently should not call this code. */
+ if (!sync_execution)
+ return;
+ /* FIXME: cagney/1999-09-27: Closely related to the above. Make
+ calls target_terminal_*() idenpotent. The event-loop GDB talking
+ to an asynchronous target with a synchronous command calls this
+ function from both event-top.c and infrun.c/infcmd.c. Once GDB
+ stops trying to transfer the terminal to the target when it
+ shouldn't this guard can go away. */
+ if (!remote_async_terminal_ours_p)
+ return;
+ delete_file_handler (input_fd);
+ remote_async_terminal_ours_p = 0;
+ initialize_sigint_signal_handler ();
+ /* NOTE: At this point we could also register our selves as the
+ recipient of all input. Any characters typed could then be
+ passed on down to the target. */
+}
+
+static void
+remote_terminal_ours (void)
+{
+ if (!target_async_permitted)
+ /* Nothing to do. */
+ return;
+
+ /* See FIXME in remote_terminal_inferior. */
+ if (!sync_execution)
+ return;
+ /* See FIXME in remote_terminal_inferior. */
+ if (remote_async_terminal_ours_p)
+ return;
+ cleanup_sigint_signal_handler (NULL);
+ add_file_handler (input_fd, stdin_event_handler, 0);
+ remote_async_terminal_ours_p = 1;
+}
+
+void
+remote_console_output (char *msg)
+{
+ char *p;
+
+ for (p = msg; p[0] && p[1]; p += 2)
+ {
+ char tb[2];
+ char c = fromhex (p[0]) * 16 + fromhex (p[1]);
+ tb[0] = c;
+ tb[1] = 0;
+ fputs_unfiltered (tb, gdb_stdtarg);
+ }
+ gdb_flush (gdb_stdtarg);
+ }
+
+typedef struct cached_reg
+{
+ int num;
+ gdb_byte data[MAX_REGISTER_SIZE];
+} cached_reg_t;
+
+DEF_VEC_O(cached_reg_t);
+
+struct stop_reply
+{
+ struct stop_reply *next;
+
+ ptid_t ptid;
+
+ struct target_waitstatus ws;
+
+ VEC(cached_reg_t) *regcache;
+
+ int stopped_by_watchpoint_p;
+ CORE_ADDR watch_data_address;
+
+ int solibs_changed;
+ int replay_event;
+};
+
+/* The list of already fetched and acknowledged stop events. */
+static struct stop_reply *stop_reply_queue;
+
+static struct stop_reply *
+stop_reply_xmalloc (void)
+{
+ struct stop_reply *r = XMALLOC (struct stop_reply);
+ r->next = NULL;
+ return r;
+}
+
+static void
+stop_reply_xfree (struct stop_reply *r)
+{
+ if (r != NULL)
+ {
+ VEC_free (cached_reg_t, r->regcache);
+ xfree (r);
+ }
+}
+
+/* Discard all pending stop replies of inferior PID. If PID is -1,
+ discard everything. */
+
+static void
+discard_pending_stop_replies (int pid)
+{
+ struct stop_reply *prev = NULL, *reply, *next;
+
+ /* Discard the in-flight notification. */
+ if (pending_stop_reply != NULL
+ && (pid == -1
+ || ptid_get_pid (pending_stop_reply->ptid) == pid))
+ {
+ stop_reply_xfree (pending_stop_reply);
+ pending_stop_reply = NULL;
+ }
+
+ /* Discard the stop replies we have already pulled with
+ vStopped. */
+ for (reply = stop_reply_queue; reply; reply = next)
+ {
+ next = reply->next;
+ if (pid == -1
+ || ptid_get_pid (reply->ptid) == pid)
+ {
+ if (reply == stop_reply_queue)
+ stop_reply_queue = reply->next;
+ else
+ prev->next = reply->next;
+
+ stop_reply_xfree (reply);
+ }
+ else
+ prev = reply;
+ }
+}
+
+/* Cleanup wrapper. */
+
+static void
+do_stop_reply_xfree (void *arg)
+{
+ struct stop_reply *r = arg;
+ stop_reply_xfree (r);
+}
+
+/* Look for a queued stop reply belonging to PTID. If one is found,
+ remove it from the queue, and return it. Returns NULL if none is
+ found. If there are still queued events left to process, tell the
+ event loop to get back to target_wait soon. */
+
+static struct stop_reply *
+queued_stop_reply (ptid_t ptid)
+{
+ struct stop_reply *it, *prev;
+ struct stop_reply head;
+
+ head.next = stop_reply_queue;
+ prev = &head;
+
+ it = head.next;
+
+ if (!ptid_equal (ptid, minus_one_ptid))
+ for (; it; prev = it, it = it->next)
+ if (ptid_equal (ptid, it->ptid))
+ break;
+
+ if (it)
+ {
+ prev->next = it->next;
+ it->next = NULL;
+ }
+
+ stop_reply_queue = head.next;
+
+ if (stop_reply_queue)
+ /* There's still at least an event left. */
+ mark_async_event_handler (remote_async_inferior_event_token);
+
+ return it;
+}
+
+/* Push a fully parsed stop reply in the stop reply queue. Since we
+ know that we now have at least one queued event left to pass to the
+ core side, tell the event loop to get back to target_wait soon. */
+
+static void
+push_stop_reply (struct stop_reply *new_event)
+{
+ struct stop_reply *event;
+
+ if (stop_reply_queue)
+ {
+ for (event = stop_reply_queue;
+ event && event->next;
+ event = event->next)
+ ;
+
+ event->next = new_event;
+ }
+ else
+ stop_reply_queue = new_event;
+
+ mark_async_event_handler (remote_async_inferior_event_token);
+}
+
+/* Returns true if we have a stop reply for PTID. */
+
+static int
+peek_stop_reply (ptid_t ptid)
+{
+ struct stop_reply *it;
+
+ for (it = stop_reply_queue; it; it = it->next)
+ if (ptid_equal (ptid, it->ptid))
+ {
+ if (it->ws.kind == TARGET_WAITKIND_STOPPED)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Parse the stop reply in BUF. Either the function succeeds, and the
+ result is stored in EVENT, or throws an error. */
+
+static void
+remote_parse_stop_reply (char *buf, struct stop_reply *event)
+{
+ struct remote_arch_state *rsa = get_remote_arch_state ();
+ ULONGEST addr;
+ char *p;
+
+ event->ptid = null_ptid;
+ event->ws.kind = TARGET_WAITKIND_IGNORE;
+ event->ws.value.integer = 0;
+ event->solibs_changed = 0;
+ event->replay_event = 0;
+ event->stopped_by_watchpoint_p = 0;
+ event->regcache = NULL;
+
+ switch (buf[0])
+ {
+ case 'T': /* Status with PC, SP, FP, ... */
+ {
+ gdb_byte regs[MAX_REGISTER_SIZE];
+
+ /* Expedited reply, containing Signal, {regno, reg} repeat. */
+ /* format is: 'Tssn...:r...;n...:r...;n...:r...;#cc', where
+ ss = signal number
+ n... = register number
+ r... = register contents
+ */
+
+ p = &buf[3]; /* after Txx */
+ while (*p)
+ {
+ char *p1;
+ char *p_temp;
+ int fieldsize;
+ LONGEST pnum = 0;
+
+ /* If the packet contains a register number, save it in
+ pnum and set p1 to point to the character following it.
+ Otherwise p1 points to p. */
+
+ /* If this packet is an awatch packet, don't parse the 'a'
+ as a register number. */
+
+ if (strncmp (p, "awatch", strlen("awatch")) != 0)
+ {
+ /* Read the ``P'' register number. */
+ pnum = strtol (p, &p_temp, 16);
+ p1 = p_temp;
+ }
+ else
+ p1 = p;
+
+ if (p1 == p) /* No register number present here. */
+ {
+ p1 = strchr (p, ':');
+ if (p1 == NULL)
+ error (_("Malformed packet(a) (missing colon): %s\n\
+Packet: '%s'\n"),
+ p, buf);
+ if (strncmp (p, "thread", p1 - p) == 0)
+ event->ptid = read_ptid (++p1, &p);
+ else if ((strncmp (p, "watch", p1 - p) == 0)
+ || (strncmp (p, "rwatch", p1 - p) == 0)
+ || (strncmp (p, "awatch", p1 - p) == 0))
+ {
+ event->stopped_by_watchpoint_p = 1;
+ p = unpack_varlen_hex (++p1, &addr);
+ event->watch_data_address = (CORE_ADDR) addr;
+ }
+ else if (strncmp (p, "library", p1 - p) == 0)
+ {
+ p1++;
+ p_temp = p1;
+ while (*p_temp && *p_temp != ';')
+ p_temp++;
+
+ event->solibs_changed = 1;
+ p = p_temp;
+ }
+ else if (strncmp (p, "replaylog", p1 - p) == 0)
+ {
+ /* NO_HISTORY event.
+ p1 will indicate "begin" or "end", but
+ it makes no difference for now, so ignore it. */
+ event->replay_event = 1;
+ p_temp = strchr (p1 + 1, ';');
+ if (p_temp)
+ p = p_temp;
+ }
+ else
+ {
+ /* Silently skip unknown optional info. */
+ p_temp = strchr (p1 + 1, ';');
+ if (p_temp)
+ p = p_temp;
+ }
+ }
+ else
+ {
+ struct packet_reg *reg = packet_reg_from_pnum (rsa, pnum);
+ cached_reg_t cached_reg;
+
+ p = p1;
+
+ if (*p != ':')
+ error (_("Malformed packet(b) (missing colon): %s\n\
+Packet: '%s'\n"),
+ p, buf);
+ ++p;
+
+ if (reg == NULL)
+ error (_("Remote sent bad register number %s: %s\n\
+Packet: '%s'\n"),
+ phex_nz (pnum, 0), p, buf);
+
+ cached_reg.num = reg->regnum;
+
+ fieldsize = hex2bin (p, cached_reg.data,
+ register_size (target_gdbarch,
+ reg->regnum));
+ p += 2 * fieldsize;
+ if (fieldsize < register_size (target_gdbarch,
+ reg->regnum))
+ warning (_("Remote reply is too short: %s"), buf);
+
+ VEC_safe_push (cached_reg_t, event->regcache, &cached_reg);
+ }
+
+ if (*p != ';')
+ error (_("Remote register badly formatted: %s\nhere: %s"),
+ buf, p);
+ ++p;
+ }
+ }
+ /* fall through */
+ case 'S': /* Old style status, just signal only. */
+ if (event->solibs_changed)
+ event->ws.kind = TARGET_WAITKIND_LOADED;
+ else if (event->replay_event)
+ event->ws.kind = TARGET_WAITKIND_NO_HISTORY;
+ else
+ {
+ event->ws.kind = TARGET_WAITKIND_STOPPED;
+ event->ws.value.sig = (enum target_signal)
+ (((fromhex (buf[1])) << 4) + (fromhex (buf[2])));
+ }
+ break;
+ case 'W': /* Target exited. */
+ case 'X':
+ {
+ char *p;
+ int pid;
+ ULONGEST value;
+
+ /* GDB used to accept only 2 hex chars here. Stubs should
+ only send more if they detect GDB supports multi-process
+ support. */
+ p = unpack_varlen_hex (&buf[1], &value);
+
+ if (buf[0] == 'W')
+ {
+ /* The remote process exited. */
+ event->ws.kind = TARGET_WAITKIND_EXITED;
+ event->ws.value.integer = value;
+ }
+ else
+ {
+ /* The remote process exited with a signal. */
+ event->ws.kind = TARGET_WAITKIND_SIGNALLED;
+ event->ws.value.sig = (enum target_signal) value;
+ }
+
+ /* If no process is specified, assume inferior_ptid. */
+ pid = ptid_get_pid (inferior_ptid);
+ if (*p == '\0')
+ ;
+ else if (*p == ';')
+ {
+ p++;
+
+ if (p == '\0')
+ ;
+ else if (strncmp (p,
+ "process:", sizeof ("process:") - 1) == 0)
+ {
+ ULONGEST upid;
+ p += sizeof ("process:") - 1;
+ unpack_varlen_hex (p, &upid);
+ pid = upid;
+ }
+ else
+ error (_("unknown stop reply packet: %s"), buf);
+ }
+ else
+ error (_("unknown stop reply packet: %s"), buf);
+ event->ptid = pid_to_ptid (pid);
+ }
+ break;
+ }
+
+ if (non_stop && ptid_equal (event->ptid, null_ptid))
+ error (_("No process or thread specified in stop reply: %s"), buf);
+}
+
+/* When the stub wants to tell GDB about a new stop reply, it sends a
+ stop notification (%Stop). Those can come it at any time, hence,
+ we have to make sure that any pending putpkt/getpkt sequence we're
+ making is finished, before querying the stub for more events with
+ vStopped. E.g., if we started a vStopped sequence immediatelly
+ upon receiving the %Stop notification, something like this could
+ happen: