#include "gdb_dirent.h"
#include "xml-support.h"
#include "terminal.h"
+#include <sys/vfs.h>
+
+#ifndef SPUFS_MAGIC
+#define SPUFS_MAGIC 0x23c9b64e
+#endif
#ifdef HAVE_PERSONALITY
# include <sys/personality.h>
static void block_child_signals (sigset_t *prev_mask);
static void restore_child_signals_mask (sigset_t *prev_mask);
+
+struct lwp_info;
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void purge_lwp_list (int pid);
+static struct lwp_info *find_lwp_pid (ptid_t ptid);
+
\f
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
linux_child_follow_fork (struct target_ops *ops, int follow_child)
{
sigset_t prev_mask;
- ptid_t last_ptid;
- struct target_waitstatus last_status;
int has_vforked;
int parent_pid, child_pid;
block_child_signals (&prev_mask);
- get_last_target_status (&last_ptid, &last_status);
- has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
- parent_pid = ptid_get_lwp (last_ptid);
+ has_vforked = (inferior_thread ()->pending_follow.kind
+ == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (inferior_ptid);
if (parent_pid == 0)
- parent_pid = ptid_get_pid (last_ptid);
- child_pid = PIDGET (last_status.value.related_pid);
+ parent_pid = ptid_get_pid (inferior_ptid);
+ child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
+
+ if (!detach_fork)
+ linux_enable_event_reporting (pid_to_ptid (child_pid));
if (! follow_child)
{
}
else
{
- struct fork_info *fp;
struct inferior *parent_inf, *child_inf;
+ struct lwp_info *lp;
+ struct cleanup *old_chain;
/* Add process to GDB's tables. */
child_inf = add_inferior (child_pid);
- parent_inf = find_inferior_pid (GET_PID (last_ptid));
+ parent_inf = current_inferior ();
child_inf->attach_flag = parent_inf->attach_flag;
copy_terminal_info (child_inf, parent_inf);
- /* Retain child fork in ptrace (stopped) state. */
- fp = find_fork_pid (child_pid);
- if (!fp)
- fp = add_fork (child_pid);
- fork_save_infrun_state (fp, 0);
+ old_chain = save_inferior_ptid ();
+
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+ lp = add_lwp (inferior_ptid);
+ lp->stopped = 1;
+
+ check_for_thread_db ();
+
+ do_cleanups (old_chain);
}
if (has_vforked)
}
else
{
- struct thread_info *last_tp = find_thread_pid (last_ptid);
struct thread_info *tp;
- char child_pid_spelling[40];
struct inferior *parent_inf, *child_inf;
-
- /* Copy user stepping state to the new inferior thread. */
- struct breakpoint *step_resume_breakpoint = last_tp->step_resume_breakpoint;
- CORE_ADDR step_range_start = last_tp->step_range_start;
- CORE_ADDR step_range_end = last_tp->step_range_end;
- struct frame_id step_frame_id = last_tp->step_frame_id;
-
- /* Otherwise, deleting the parent would get rid of this
- breakpoint. */
- last_tp->step_resume_breakpoint = NULL;
+ struct lwp_info *lp;
/* Before detaching from the parent, remove all breakpoints from it. */
remove_breakpoints ();
child_inf = add_inferior (child_pid);
- parent_inf = find_inferior_pid (GET_PID (last_ptid));
+ parent_inf = current_inferior ();
child_inf->attach_flag = parent_inf->attach_flag;
copy_terminal_info (child_inf, parent_inf);
if (has_vforked)
{
+ struct lwp_info *parent_lwp;
+
linux_parent_pid = parent_pid;
+
+ /* Get rid of the inferior on the core side as well. */
+ inferior_ptid = null_ptid;
detach_inferior (parent_pid);
- }
- else if (!detach_fork)
- {
- struct fork_info *fp;
- /* Retain parent fork in ptrace (stopped) state. */
- fp = find_fork_pid (parent_pid);
- if (!fp)
- fp = add_fork (parent_pid);
- fork_save_infrun_state (fp, 0);
- /* Also add an entry for the child fork. */
- fp = find_fork_pid (child_pid);
- if (!fp)
- fp = add_fork (child_pid);
- fork_save_infrun_state (fp, 0);
+ /* Also get rid of all its lwps. We will detach from this
+ inferior soon-ish, but, we will still get an exit event
+ reported through waitpid when it exits. If we didn't get
+ rid of the lwps from our list, we would end up reporting
+ the inferior exit to the core, which would then try to
+ mourn a non-existing (from the core's perspective)
+ inferior. */
+ parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
+ purge_lwp_list (GET_PID (parent_lwp->ptid));
+ linux_parent_pid = parent_pid;
}
- else
+ else if (detach_fork)
target_detach (NULL, 0);
inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+ lp = add_lwp (inferior_ptid);
+ lp->stopped = 1;
- linux_nat_switch_fork (inferior_ptid);
check_for_thread_db ();
-
- tp = inferior_thread ();
- tp->step_resume_breakpoint = step_resume_breakpoint;
- tp->step_range_start = step_range_start;
- tp->step_range_end = step_range_end;
- tp->step_frame_id = step_frame_id;
-
- /* Reset breakpoints in the child as appropriate. */
- follow_inferior_reset_breakpoints ();
}
restore_child_signals_mask (&prev_mask);
return NULL;
}
-/* Update our internal state when changing from one fork (checkpoint,
- et cetera) to another indicated by NEW_PTID. We can only switch
- single-threaded applications, so we only create one new LWP, and
- the previous list is discarded. */
+/* Update our internal state when changing from one checkpoint to
+ another indicated by NEW_PTID. We can only switch single-threaded
+ applications, so we only create one new LWP, and the previous list
+ is discarded. */
void
linux_nat_switch_fork (ptid_t new_ptid)
{
struct lwp_info *lp;
- init_lwp_list ();
+ purge_lwp_list (GET_PID (inferior_ptid));
+
lp = add_lwp (new_ptid);
lp->stopped = 1;
- init_thread_list ();
- add_thread_silent (new_ptid);
+ /* This changes the thread's ptid while preserving the gdb thread
+ num. Also changes the inferior pid, while preserving the
+ inferior num. */
+ thread_change_ptid (inferior_ptid, new_ptid);
+
+ /* We've just told GDB core that the thread changed target id, but,
+ in fact, it really is a different thread, with different register
+ contents. */
+ registers_changed ();
}
/* Handle the exit of a single thread LP. */
static void
exit_lwp (struct lwp_info *lp)
{
- struct thread_info *th = find_thread_pid (lp->ptid);
+ struct thread_info *th = find_thread_ptid (lp->ptid);
if (th)
{
have the last signal recorded in
thread_info->stop_signal. */
- struct thread_info *tp = find_thread_pid (lp->ptid);
+ struct thread_info *tp = find_thread_ptid (lp->ptid);
signo = tp->stop_signal;
}
{
if (GET_LWP (lp->ptid) == GET_LWP (last_ptid))
{
- struct thread_info *tp = find_thread_pid (lp->ptid);
+ struct thread_info *tp = find_thread_ptid (lp->ptid);
if (tp->stop_signal != TARGET_SIGNAL_0
&& signal_pass_state (tp->stop_signal))
*status = W_STOPCODE (target_signal_to_host (tp->stop_signal));
ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
+ if (event == PTRACE_EVENT_FORK
+ && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+ {
+ struct fork_info *fp;
+
+ /* Handle checkpointing by linux-fork.c here as a special
+ case. We don't want the follow-fork-mode or 'catch fork'
+ to interfere with this. */
+
+ /* This won't actually modify the breakpoint list, but will
+ physically remove the breakpoints from the child. */
+ detach_breakpoints (new_pid);
+
+ /* Retain child fork in ptrace (stopped) state. */
+ fp = find_fork_pid (new_pid);
+ if (!fp)
+ fp = add_fork (new_pid);
+
+ /* Report as spurious, so that infrun doesn't want to follow
+ this fork. We're actually doing an infcall in
+ linux-fork.c. */
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+ linux_enable_event_reporting (pid_to_ptid (new_pid));
+
+ /* Report the stop to the core. */
+ return 0;
+ }
+
if (event == PTRACE_EVENT_FORK)
ourstatus->kind = TARGET_WAITKIND_FORKED;
else if (event == PTRACE_EVENT_VFORK)
if (event == PTRACE_EVENT_EXEC)
{
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LHEW: Got exec event from LWP %ld\n",
+ GET_LWP (lp->ptid));
+
ourstatus->kind = TARGET_WAITKIND_EXECD;
ourstatus->value.execd_pathname
= xstrdup (linux_child_pid_to_exec_file (pid));
return linux_xfer_siginfo (ops, object, annex, readbuf, writebuf,
offset, len);
+ /* The target is connected but no live inferior is selected. Pass
+ this request down to a lower stratum (e.g., the executable
+ file). */
+ if (object == TARGET_OBJECT_MEMORY && ptid_equal (inferior_ptid, null_ptid))
+ return 0;
+
old_chain = save_inferior_ptid ();
if (is_lwp (inferior_ptid))
if (info_verbose)
{
fprintf_filtered (gdb_stdout,
- "Save segment, %lld bytes at 0x%s (%c%c%c)",
- size, paddr_nz (addr),
+ "Save segment, %lld bytes at %s (%c%c%c)",
+ size, paddress (target_gdbarch, addr),
read ? 'r' : ' ',
write ? 'w' : ' ', exec ? 'x' : ' ');
if (filename[0])
gdb_gregset_t gregs;
gdb_fpregset_t fpregs;
unsigned long lwp = ptid_get_lwp (ptid);
- struct regcache *regcache = get_thread_regcache (ptid);
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch *gdbarch = target_gdbarch;
+ struct regcache *regcache = get_thread_arch_regcache (ptid, gdbarch);
const struct regset *regset;
int core_regset_p;
struct cleanup *old_chain;
return 0;
}
+/* Enumerate spufs IDs for process PID. */
+
+static void
+iterate_over_spus (int pid, void (*callback) (void *, int), void *data)
+{
+ char path[128];
+ DIR *dir;
+ struct dirent *entry;
+
+ xsnprintf (path, sizeof path, "/proc/%d/fd", pid);
+ dir = opendir (path);
+ if (!dir)
+ return;
+
+ rewinddir (dir);
+ while ((entry = readdir (dir)) != NULL)
+ {
+ struct stat st;
+ struct statfs stfs;
+ int fd;
+
+ fd = atoi (entry->d_name);
+ if (!fd)
+ continue;
+
+ xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd);
+ if (stat (path, &st) != 0)
+ continue;
+ if (!S_ISDIR (st.st_mode))
+ continue;
+
+ if (statfs (path, &stfs) != 0)
+ continue;
+ if (stfs.f_type != SPUFS_MAGIC)
+ continue;
+
+ callback (data, fd);
+ }
+
+ closedir (dir);
+}
+
+/* Generate corefile notes for SPU contexts. */
+
+struct linux_spu_corefile_data
+{
+ bfd *obfd;
+ char *note_data;
+ int *note_size;
+};
+
+static void
+linux_spu_corefile_callback (void *data, int fd)
+{
+ struct linux_spu_corefile_data *args = data;
+ int i;
+
+ static const char *spu_files[] =
+ {
+ "object-id",
+ "mem",
+ "regs",
+ "fpcr",
+ "lslr",
+ "decr",
+ "decr_status",
+ "signal1",
+ "signal1_type",
+ "signal2",
+ "signal2_type",
+ "event_mask",
+ "event_status",
+ "mbox_info",
+ "ibox_info",
+ "wbox_info",
+ "dma_info",
+ "proxydma_info",
+ };
+
+ for (i = 0; i < sizeof (spu_files) / sizeof (spu_files[0]); i++)
+ {
+ char annex[32], note_name[32];
+ gdb_byte *spu_data;
+ LONGEST spu_len;
+
+ xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[i]);
+ spu_len = target_read_alloc (¤t_target, TARGET_OBJECT_SPU,
+ annex, &spu_data);
+ if (spu_len > 0)
+ {
+ xsnprintf (note_name, sizeof note_name, "SPU/%s", annex);
+ args->note_data = elfcore_write_note (args->obfd, args->note_data,
+ args->note_size, note_name,
+ NT_SPU, spu_data, spu_len);
+ xfree (spu_data);
+ }
+ }
+}
+
+static char *
+linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size)
+{
+ struct linux_spu_corefile_data args;
+ args.obfd = obfd;
+ args.note_data = note_data;
+ args.note_size = note_size;
+
+ iterate_over_spus (PIDGET (inferior_ptid),
+ linux_spu_corefile_callback, &args);
+
+ return args.note_data;
+}
+
/* Fills the "to_make_corefile_note" target vector. Builds the note
section for a corefile, and returns it in a malloc buffer. */
xfree (auxv);
}
+ note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size);
+
make_cleanup (xfree, note_data);
return note_data;
}
cleanup = make_cleanup_fclose (procfile);
printf_filtered (_("Mapped address spaces:\n\n"));
- if (gdbarch_addr_bit (current_gdbarch) == 32)
+ if (gdbarch_addr_bit (target_gdbarch) == 32)
{
printf_filtered ("\t%10s %10s %10s %10s %7s\n",
"Start Addr",
a generic local_address_string instead to print out
the addresses; that makes sense to me, too. */
- if (gdbarch_addr_bit (current_gdbarch) == 32)
+ if (gdbarch_addr_bit (target_gdbarch) == 32)
{
printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n",
(unsigned long) addr, /* FIXME: pr_addr */
return ret;
}
+
+/* Enumerate spufs IDs for process PID. */
+static LONGEST
+spu_enumerate_spu_ids (int pid, gdb_byte *buf, ULONGEST offset, LONGEST len)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+ LONGEST pos = 0;
+ LONGEST written = 0;
+ char path[128];
+ DIR *dir;
+ struct dirent *entry;
+
+ xsnprintf (path, sizeof path, "/proc/%d/fd", pid);
+ dir = opendir (path);
+ if (!dir)
+ return -1;
+
+ rewinddir (dir);
+ while ((entry = readdir (dir)) != NULL)
+ {
+ struct stat st;
+ struct statfs stfs;
+ int fd;
+
+ fd = atoi (entry->d_name);
+ if (!fd)
+ continue;
+
+ xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd);
+ if (stat (path, &st) != 0)
+ continue;
+ if (!S_ISDIR (st.st_mode))
+ continue;
+
+ if (statfs (path, &stfs) != 0)
+ continue;
+ if (stfs.f_type != SPUFS_MAGIC)
+ continue;
+
+ if (pos >= offset && pos + 4 <= offset + len)
+ {
+ store_unsigned_integer (buf + pos - offset, 4, byte_order, fd);
+ written += 4;
+ }
+ pos += 4;
+ }
+
+ closedir (dir);
+ return written;
+}
+
+/* Implement the to_xfer_partial interface for the TARGET_OBJECT_SPU
+ object type, using the /proc file system. */
+static LONGEST
+linux_proc_xfer_spu (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ char buf[128];
+ int fd = 0;
+ int ret = -1;
+ int pid = PIDGET (inferior_ptid);
+
+ if (!annex)
+ {
+ if (!readbuf)
+ return -1;
+ else
+ return spu_enumerate_spu_ids (pid, readbuf, offset, len);
+ }
+
+ xsnprintf (buf, sizeof buf, "/proc/%d/fd/%s", pid, annex);
+ fd = open (buf, writebuf? O_WRONLY : O_RDONLY);
+ if (fd <= 0)
+ return -1;
+
+ if (offset != 0
+ && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+ {
+ close (fd);
+ return 0;
+ }
+
+ if (writebuf)
+ ret = write (fd, writebuf, (size_t) len);
+ else if (readbuf)
+ ret = read (fd, readbuf, (size_t) len);
+
+ close (fd);
+ return ret;
+}
+
+
/* Parse LINE as a signal set and add its set bits to SIGS. */
static void
return linux_nat_xfer_osdata (ops, object, annex, readbuf, writebuf,
offset, len);
+ if (object == TARGET_OBJECT_SPU)
+ return linux_proc_xfer_spu (ops, object, annex, readbuf, writebuf,
+ offset, len);
+
+ /* GDB calculates all the addresses in possibly larget width of the address.
+ Address width needs to be masked before its final use - either by
+ linux_proc_xfer_partial or inf_ptrace_xfer_partial.
+
+ Compare ADDR_BIT first to avoid a compiler warning on shift overflow. */
+
+ if (object == TARGET_OBJECT_MEMORY)
+ {
+ int addr_bit = gdbarch_addr_bit (target_gdbarch);
+
+ if (addr_bit < (sizeof (ULONGEST) * HOST_CHAR_BIT))
+ offset &= ((ULONGEST) 1 << addr_bit) - 1;
+ }
+
xfer = linux_proc_xfer_partial (ops, object, annex, readbuf, writebuf,
offset, len);
if (xfer != 0)
/* True if we want to support multi-process. To be removed when GDB
supports multi-exec. */
-int linux_multi_process = 0;
+int linux_multi_process = 1;
static int
linux_nat_supports_multi_process (void)
if (debug_linux_nat)
{
- if (find_thread_pid (lwp->ptid)->stop_requested)
+ if (find_thread_ptid (lwp->ptid)->stop_requested)
fprintf_unfiltered (gdb_stdlog, "\
LNSL: already stopped/stop_requested %s\n",
target_pid_to_str (lwp->ptid));