blob: 2f7da779676fc071dc674ed598f3d362afd50078 [file] [log] [blame]
/* Async support for GDB, the GNU debugger.
Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2006
Free Software Foundation, Inc.
Original code from Apple Computer, Inc.
Modified by Nick Roberts <nickrob@snap.net.nz>
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 2 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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#include "defs.h"
#include "inferior.h"
#include "objfiles.h"
#include "event-top.h"
#include "async-nat-inferior.h"
gdb_inferior_status *gdb_status = NULL;
enum gdb_source_type
{
NEXT_SOURCE_NONE = 0x0,
NEXT_SOURCE_SIGNAL = 0x1,
};
struct gdb_pending_event
{
enum gdb_source_type type;
unsigned char *buf;
struct gdb_pending_event *next;
};
struct gdb_pending_event *pending_event_chain, *pending_event_tail;
static enum gdb_source_type gdb_fetch_event (struct
gdb_inferior_status
*inferior,
unsigned char *buf,
size_t len,
unsigned int flags,
int timeout);
static int gdb_service_event (enum gdb_source_type source,
unsigned char *buf,
struct target_waitstatus *status);
static void gdb_handle_signal (gdb_signal_thread_message *msg,
struct target_waitstatus *status);
static void gdb_add_to_pending_events (enum gdb_source_type,
unsigned char *buf);
static void gdb_pending_event_handler (void *data);
static void gdb_inferior_reset (gdb_inferior_status *s);
static void
gdb_handle_signal (gdb_signal_thread_message *msg,
struct target_waitstatus *status)
{
if (msg->pid != gdb_status->pid)
{
warning ("gdb_handle_signal: signal message was for pid %d, "
"not for inferior process (pid %d)\n",
msg->pid, gdb_status->pid);
return;
}
if (WIFEXITED (msg->status))
{
status->kind = TARGET_WAITKIND_EXITED;
status->value.integer = WEXITSTATUS (msg->status);
return;
}
if (!WIFSTOPPED (msg->status))
{
status->kind = TARGET_WAITKIND_SIGNALLED;
status->value.sig = target_signal_from_host (WTERMSIG (msg->status));
return;
}
gdb_status->stopped_in_ptrace = 1;
status->kind = TARGET_WAITKIND_STOPPED;
status->value.sig = target_signal_from_host (WSTOPSIG (msg->status));
}
static void
gdb_add_to_port_set (struct gdb_inferior_status *inferior,
fd_set * fds, int flags)
{
FD_ZERO (fds);
if ((flags & NEXT_SOURCE_SIGNAL)
&& inferior->signal_status.receive_fd > 0)
{
FD_SET (inferior->signal_status.receive_fd, fds);
}
}
/* TIMEOUT is either -1, 0, or greater than 0.
For 0, check if there is anything to read, but don't block.
For -1, block until there is something to read.
For >0, block at least the specified number of microseconds, or until there
is something to read.
The kernel doesn't give better than ~1HZ (0.01 sec) resolution, so
don't use this as a high accuracy timer. */
static enum gdb_source_type
gdb_fetch_event (struct gdb_inferior_status *inferior,
unsigned char *buf, size_t len,
unsigned int flags, int timeout)
{
fd_set fds;
int fd, ret;
struct timeval tv;
// CHECK_FATAL (len >= sizeof (gdb_signal_thread_message));
tv.tv_sec = 0;
tv.tv_usec = timeout;
gdb_add_to_port_set (inferior, &fds, flags);
for (;;)
{
if (timeout == -1)
{
ret = select (FD_SETSIZE, &fds, NULL, NULL, NULL);
}
else
{
ret = select (FD_SETSIZE, &fds, NULL, NULL, &tv);
}
if ((ret < 0) && (errno == EINTR))
{
continue;
}
if (ret < 0)
{
internal_error (__FILE__, __LINE__, "unable to select: %s",
strerror (errno));
}
if (ret == 0)
{
return NEXT_SOURCE_NONE;
}
break;
}
fd = inferior->signal_status.receive_fd;
if (fd > 0 && FD_ISSET (fd, &fds))
{
read (fd, buf, sizeof (gdb_signal_thread_message));
return NEXT_SOURCE_SIGNAL;
}
return NEXT_SOURCE_NONE;
}
/* This takes the data from an event and puts it on the tail of the
"pending event" chain. */
static void
gdb_add_to_pending_events (enum gdb_source_type type,
unsigned char *buf)
{
struct gdb_pending_event *new_event;
new_event = (struct gdb_pending_event *)
xmalloc (sizeof (struct gdb_pending_event));
new_event->type = type;
if (type == NEXT_SOURCE_SIGNAL)
{
gdb_signal_thread_message *mssg;
mssg = (gdb_signal_thread_message *)
xmalloc (sizeof (gdb_signal_thread_message));
memcpy (mssg, buf, sizeof (gdb_signal_thread_message));
/*inferior_debug (1,
"gdb_add_to_pending_events: adding a signal event to the pending events.\n"); */
new_event->buf = (void *) mssg;
}
new_event->next = NULL;
if (pending_event_chain == NULL)
{
pending_event_chain = new_event;
pending_event_tail = new_event;
}
else
{
pending_event_tail->next = new_event;
pending_event_tail = new_event;
}
}
void
gdb_clear_pending_events ()
{
struct gdb_pending_event *event_ptr = pending_event_chain;
while (event_ptr != NULL)
{
pending_event_chain = event_ptr->next;
xfree (event_ptr->buf);
xfree (event_ptr);
event_ptr = pending_event_chain;
}
}
/* This extracts the top of the pending event chain and posts a gdb event
with its content to the gdb event queue. Returns 0 if there were no
pending events to be posted, 1 otherwise. */
int
gdb_post_pending_event (void)
{
struct gdb_pending_event *event;
if (pending_event_chain == NULL)
{
//inferior_debug (1, "gdb_post_pending_event: no events to post\n");
return 0;
}
else
{
event = pending_event_chain;
pending_event_chain = pending_event_chain->next;
if (pending_event_chain == NULL)
pending_event_tail = NULL;
gdb_queue_event (gdb_pending_event_handler, (void *) event, HEAD);
return 1;
}
}
static void
gdb_pending_event_handler (void *data)
{
async_client_callback (INF_REG_EVENT, data);
}
static int
gdb_service_event (enum gdb_source_type source,
unsigned char *buf, struct target_waitstatus *status)
{
if (source == NEXT_SOURCE_SIGNAL)
gdb_handle_signal ((gdb_signal_thread_message *) buf, status);
else
{
error ("got message from unknown source: 0x%08x\n", source);
return 0;
}
return 1;
}
/* This drains the event sources. The first event found is directly
handled. The rest are placed on the pending events queue, to be
handled the next time that the inferior is "run".
Returns: The number of events found. */
int
gdb_process_events (struct gdb_inferior_status *inferior,
struct target_waitstatus *status,
int timeout, int service_first_event)
{
enum gdb_source_type source;
unsigned char buf[1024];
int event_count;
// CHECK_FATAL (status->kind == TARGET_WAITKIND_SPURIOUS);
source = gdb_fetch_event (inferior, buf, sizeof (buf),
NEXT_SOURCE_SIGNAL, timeout);
if (source == NEXT_SOURCE_NONE)
{
return 0;
}
event_count = 1;
if (service_first_event)
{
if (gdb_service_event (source, buf, status) == 0)
return 0;
}
else
{
gdb_add_to_pending_events (source, buf);
}
/* FIXME: we want to poll in gdb_fetch_event because otherwise we
arbitrarily wait however long the wait quanta for select is
(seemingly ~.01 sec). However, if we do this we aren't giving
the mach exception thread a chance to run, and see if there are
any more exceptions available. Normally this is okay, because
there really IS only one message, but to be correct we need to
use some thread synchronization. */
for (;;)
{
source = gdb_fetch_event (inferior, buf, sizeof (buf),
NEXT_SOURCE_SIGNAL, 0);
if (source == NEXT_SOURCE_NONE)
{
break;
}
else
{
event_count++;
/* Stuff the remaining events onto the pending_events queue.
These will be dispatched when we run again. */
/* PENDING_EVENTS */
gdb_add_to_pending_events (source, buf);
}
}
return event_count;
}
ptid_t
gdb_process_pending_event (struct gdb_inferior_status *ns,
struct target_waitstatus *status,
gdb_client_data client_data)
{
struct gdb_pending_event *event
= (struct gdb_pending_event *) client_data;
//inferior_debug (1, "Processing pending event type: %d\n", event->type);
gdb_service_event (event->type, (unsigned char *) event->buf, status);
// printf ("IN GDB_PROCESS_PENDING_EVENT %s\n", event->buf);
return ptid_build (gdb_status->pid, gdb_status->pid, 0);
}
void
gdb_create_inferior (struct gdb_inferior_status *inferior, int pid)
{
gdb_inferior_destroy (inferior);
gdb_inferior_reset (inferior);
inferior->pid = pid;
inferior->attached_in_ptrace = 0;
inferior->stopped_in_ptrace = 0;
}
void
async_terminal_inferior (void)
{
terminal_inferior ();
if (!sync_execution)
return;
delete_file_handler (input_fd);
}
void
async_terminal_ours (void)
{
terminal_ours ();
if (!sync_execution)
return;
add_file_handler (input_fd, stdin_event_handler, 0);
}
static void
gdb_inferior_reset (gdb_inferior_status *s)
{
s->pid = 0;
s->attached_in_ptrace = 0;
s->stopped_in_ptrace = 0;
gdb_signal_thread_init (&s->signal_status);
}
void
gdb_inferior_destroy (gdb_inferior_status *s)
{
gdb_signal_thread_destroy (&s->signal_status);
s->pid = 0;
gdb_inferior_reset (s);
}
void
_initialize_gdb_inferior ()
{
gdb_status = (struct gdb_inferior_status *)
xmalloc (sizeof (struct gdb_inferior_status));
gdb_inferior_reset (gdb_status);
}