This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfc] [15/18] Cell multi-arch: Per-frame architecture for the Cell debugger


Hello,

up to now, multi-arch debugging only implemented per-thread architecture.
With this patch, we get to support per-frame architecture.

This works by installing ARCH_FRAME sniffers with a non-NULL prev_arch
callback.  We have one on the SPU side, that detects that we've reached
the top of stack on the SPU, and continues unwinding to the PowerPC
code that started execution of this SPU context.

The second sniffer sits on the PowerPC side, and detects that we have
interrupted SPU-side execution due to a PPU-assisted call (or a call
to a signal handler).  This actually requires assistance by the libspe
library, which defines a per-thread linked list of interrupted SPU
contexts.  The GDB sniffer goes through that list and checks whether
the current frame is on that list -- if so, it injects the context
specified by libspe as the interrupted context.

To speed up access to that thread-local variable, the code caches the
lm_addr and offset values associated with it, as well as the address
of the instance of that thread-local variable in the current thread.

The PowerPC-side sniffer is installed only if the target supports Cell.
The SPU-side sniffer is installed unconditionally, but does not trigger
if we're debugging a SPU stand-alone executable, as we do not want to
backtrace back to the PowerPC side in that case (and we cannot if we're
running on the SPU native target anyway).

Bye,
Ulrich



ChangeLog:

	* ppc-linux-tdep.c: Include "exceptions.h", "arch-utils.h",
	and "spu-tdep.h".
	(spe_context_lm_addr, spe_context_offset): New static variables.
	(spe_context_cache_ptid, spe_context_cache_address): Likewise.
	(ppc_linux_new_objfile): New function.
	(ppc_linux_spe_context): Likewise.
	(struct ppu2spu_cache, struct ppu2spu_data): New data types.
	(ppu2spu_prev_arch, ppu2spu_this_id, ppu2spu_prev_register,
	ppu2spu_unwind_register, ppu2spu_sniffer,
	ppu2spu_dealloc_cache): New functions.
	(ppu2spu_unwind): New static variable.
	(ppc_linux_init_abi): Install cross-architecture unwinder.
	(_initialize_ppc_linux_tdep): Attach to new_objfile observer.

	* spu-tdep.c (struct spu2ppu_cache): New data type.
	(spu2ppu_prev_arch, spu2ppu_this_id, spu2ppu_prev_register,
	spu2ppu_sniffer, spu2ppu_dealloc_cache): New functions.
	(spu2ppu_unwind): New static variable.
	(spu_gdbarch_init): Install cross-architecture unwinder.



Index: src/gdb/ppc-linux-tdep.c
===================================================================
--- src.orig/gdb/ppc-linux-tdep.c
+++ src/gdb/ppc-linux-tdep.c
@@ -41,6 +41,9 @@
 #include "observer.h"
 #include "auxv.h"
 #include "elf/common.h"
+#include "exceptions.h"
+#include "arch-utils.h"
+#include "spu-tdep.h"
 
 #include "features/rs6000/powerpc-32l.c"
 #include "features/rs6000/powerpc-altivec32l.c"
@@ -1112,6 +1115,249 @@ ppc_linux_core_read_description (struct 
     }
 }
 
+
+/* Cell/B.E. active SPE context tracking support.  */
+
+static CORE_ADDR spe_context_lm_addr = 0;
+static CORE_ADDR spe_context_offset = 0;
+
+static ptid_t spe_context_cache_ptid;
+static CORE_ADDR spe_context_cache_address;
+
+/* Callback for new objfiles.  We check whether we've loaded a version
+   of libspe2 that provides the __spe_current_active_context variable.  */
+static void
+ppc_linux_new_objfile (struct objfile *objfile)
+{
+  struct minimal_symbol *sym;
+
+  if (!objfile)
+    {
+      spe_context_lm_addr = 0;
+      spe_context_offset = 0;
+      spe_context_cache_ptid = minus_one_ptid;
+      spe_context_cache_address = 0;
+      return;
+    }
+
+  sym = lookup_minimal_symbol ("__spe_current_active_context", NULL, objfile);
+  if (sym)
+    {
+      spe_context_lm_addr = svr4_fetch_objfile_link_map (objfile);
+      spe_context_offset = SYMBOL_VALUE_ADDRESS (sym);
+      spe_context_cache_ptid = minus_one_ptid;
+      spe_context_cache_address = 0;
+      return;
+    }
+}
+
+/* Retrieve contents of the N'th element in the current thread's
+   linked SPE context list into ID and NPC.  Return the address of
+   said context element, or 0 if not found.  */
+static CORE_ADDR
+ppc_linux_spe_context (int wordsize, int n, int *id, unsigned int *npc)
+{
+  CORE_ADDR spe_context = 0;
+  gdb_byte buf[16];
+  int i;
+
+  /* Quick exit if we have not found __spe_current_active_context.  */
+  if (!spe_context_lm_addr)
+    return 0;
+
+  /* Look up cached address of thread-local variable.  */
+  if (!ptid_equal (spe_context_cache_ptid, inferior_ptid))
+    {
+      volatile struct gdb_exception ex;
+
+      if (!target_get_thread_local_address_p ())
+	return 0;
+
+      TRY_CATCH (ex, RETURN_MASK_ERROR)
+	{
+	  spe_context_cache_address
+	    = target_get_thread_local_address
+	       (inferior_ptid, spe_context_lm_addr, spe_context_offset);
+
+	  spe_context_cache_ptid = inferior_ptid;
+	}
+
+      if (ex.reason < 0)
+	return 0;
+    }
+
+  /* Read variable value.  */
+  if (target_read_memory (spe_context_cache_address, buf, wordsize) == 0)
+    spe_context = extract_unsigned_integer (buf, wordsize);
+
+  /* Cyle through to N'th linked list element.  */
+  for (i = 0; i < n && spe_context; i++)
+    if (target_read_memory (spe_context + align_up (12, wordsize),
+			    buf, wordsize) == 0)
+      spe_context = extract_unsigned_integer (buf, wordsize);
+    else
+      spe_context = 0;
+
+  /* Read current context.  */
+  if (spe_context
+      && target_read_memory (spe_context, buf, 12) != 0)
+    spe_context = 0;
+
+  /* Extract data elements.  */
+  if (spe_context)
+    {
+      if (id)
+	*id = extract_signed_integer (buf, 4);
+      if (npc)
+	*npc = extract_unsigned_integer (buf + 4, 4);
+    }
+
+  return spe_context;
+}
+
+
+/* Cell/B.E. cross-architecture unwinder support.  */
+
+struct ppu2spu_cache
+{
+  struct frame_id frame_id;
+  struct regcache *regcache;
+};
+
+static struct gdbarch *
+ppu2spu_prev_arch (struct frame_info *this_frame, void **this_cache)
+{
+  struct ppu2spu_cache *cache = *this_cache;
+  return get_regcache_arch (cache->regcache);
+}
+
+static void
+ppu2spu_this_id (struct frame_info *this_frame,
+		 void **this_cache, struct frame_id *this_id)
+{
+  struct ppu2spu_cache *cache = *this_cache;
+  *this_id = cache->frame_id;
+}
+
+static struct value *
+ppu2spu_prev_register (struct frame_info *this_frame,
+		       void **this_cache, int regnum)
+{
+  struct ppu2spu_cache *cache = *this_cache;
+  struct gdbarch *gdbarch = get_regcache_arch (cache->regcache);
+  gdb_byte *buf;
+
+  buf = alloca (register_size (gdbarch, regnum));
+  regcache_cooked_read (cache->regcache, regnum, buf);
+  return frame_unwind_got_bytes (this_frame, regnum, buf);
+}
+
+struct ppu2spu_data
+{
+  int id;
+  unsigned int npc;
+  gdb_byte gprs[128*16];
+};
+
+static int
+ppu2spu_unwind_register (void *src, int regnum, gdb_byte *buf)
+{
+  struct ppu2spu_data *data = src;
+
+  if (regnum >= 0 && regnum < SPU_NUM_GPRS)
+    memcpy (buf, data->gprs + 16*regnum, 16);
+  else if (regnum == SPU_ID_REGNUM)
+    store_unsigned_integer (buf, 4, data->id);
+  else if (regnum == SPU_PC_REGNUM)
+    store_unsigned_integer (buf, 4, data->npc);
+  else
+    return 0;
+
+  return 1;
+}
+
+static int
+ppu2spu_sniffer (const struct frame_unwind *self,
+		 struct frame_info *this_frame, void **this_prologue_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  struct ppu2spu_data data;
+  struct frame_info *fi;
+  CORE_ADDR base, func, backchain, spe_context;
+  gdb_byte buf[8];
+  int n = 0;
+
+  /* Count the number of SPU contexts already in the frame chain.  */
+  for (fi = get_next_frame (this_frame); fi; fi = get_next_frame (fi))
+    if (get_frame_type (fi) == ARCH_FRAME
+	&& gdbarch_bfd_arch_info (get_frame_arch (fi))->arch == bfd_arch_spu)
+      n++;
+
+  base = get_frame_sp (this_frame);
+  func = get_frame_pc (this_frame);
+  if (target_read_memory (base, buf, tdep->wordsize))
+    return 0;
+  backchain = extract_unsigned_integer (buf, tdep->wordsize);
+
+  spe_context = ppc_linux_spe_context (tdep->wordsize, n, &data.id, &data.npc);
+  if (spe_context && base <= spe_context && spe_context < backchain)
+    {
+      struct gdbarch *spu_gdbarch;
+      char annex[32];
+
+      /* Find gdbarch for SPU.  */
+      struct gdbarch_info info;
+      gdbarch_info_init (&info);
+      info.bfd_arch_info = bfd_lookup_arch (bfd_arch_spu, bfd_mach_spu);
+      info.byte_order = BFD_ENDIAN_BIG;
+      info.osabi = GDB_OSABI_LINUX;
+      info.tdep_info = (void *) &data.id;
+      spu_gdbarch = gdbarch_find_by_info (info);
+      if (!spu_gdbarch)
+	return 0;
+
+      xsnprintf (annex, sizeof annex, "%d/regs", data.id);
+      if (target_read (&current_target, TARGET_OBJECT_SPU, annex,
+		       data.gprs, 0, sizeof data.gprs)
+	  == sizeof data.gprs)
+	{
+	  struct ppu2spu_cache *cache
+	    = FRAME_OBSTACK_CALLOC (1, struct ppu2spu_cache);
+
+	  struct regcache *regcache = regcache_xmalloc (spu_gdbarch);
+	  struct cleanup *cleanups = make_cleanup_regcache_xfree (regcache);
+	  regcache_save (regcache, ppu2spu_unwind_register, &data);
+	  discard_cleanups (cleanups);
+
+	  cache->frame_id = frame_id_build (base, func);
+	  cache->regcache = regcache;
+	  *this_prologue_cache = cache;
+	  return 1;
+	}
+    }
+
+  return 0;
+}
+
+static void
+ppu2spu_dealloc_cache (struct frame_info *self, void *this_cache)
+{
+  struct ppu2spu_cache *cache = this_cache;
+  regcache_xfree (cache->regcache);
+}
+
+static const struct frame_unwind ppu2spu_unwind = {
+  ARCH_FRAME,
+  ppu2spu_this_id,
+  ppu2spu_prev_register,
+  NULL,
+  ppu2spu_sniffer,
+  ppu2spu_dealloc_cache,
+  ppu2spu_prev_arch,
+};
+
+
 static void
 ppc_linux_init_abi (struct gdbarch_info info,
                     struct gdbarch *gdbarch)
@@ -1228,6 +1474,9 @@ ppc_linux_init_abi (struct gdbarch_info 
       /* Cell/B.E. multi-architecture support.  */
       set_spu_solib_ops (gdbarch);
 
+      /* Cell/B.E. cross-architecture unwinder support.  */
+      frame_unwind_prepend_unwinder (gdbarch, &ppu2spu_unwind);
+
       /* The default displaced_step_at_entry_point doesn't work for
 	 SPU stand-alone executables.  */
       set_gdbarch_displaced_step_location (gdbarch,
@@ -1250,6 +1499,9 @@ _initialize_ppc_linux_tdep (void)
   /* Attach to inferior_created observer.  */
   observer_attach_inferior_created (ppc_linux_inferior_created);
 
+  /* Add ourselves to objfile event chain.  */
+  observer_attach_new_objfile (ppc_linux_new_objfile);
+
   /* Initialize the Linux target descriptions.  */
   initialize_tdesc_powerpc_32l ();
   initialize_tdesc_powerpc_altivec32l ();
Index: src/gdb/spu-tdep.c
===================================================================
--- src.orig/gdb/spu-tdep.c
+++ src/gdb/spu-tdep.c
@@ -1052,6 +1052,108 @@ spu_write_pc (struct regcache *regcache,
 }
 
 
+/* Cell/B.E. cross-architecture unwinder support.  */
+
+struct spu2ppu_cache
+{
+  struct frame_id frame_id;
+  struct regcache *regcache;
+};
+
+static struct gdbarch *
+spu2ppu_prev_arch (struct frame_info *this_frame, void **this_cache)
+{
+  struct spu2ppu_cache *cache = *this_cache;
+  return get_regcache_arch (cache->regcache);
+}
+
+static void
+spu2ppu_this_id (struct frame_info *this_frame,
+		 void **this_cache, struct frame_id *this_id)
+{
+  struct spu2ppu_cache *cache = *this_cache;
+  *this_id = cache->frame_id;
+}
+
+static struct value *
+spu2ppu_prev_register (struct frame_info *this_frame,
+		       void **this_cache, int regnum)
+{
+  struct spu2ppu_cache *cache = *this_cache;
+  struct gdbarch *gdbarch = get_regcache_arch (cache->regcache);
+  gdb_byte *buf;
+
+  buf = alloca (register_size (gdbarch, regnum));
+  regcache_cooked_read (cache->regcache, regnum, buf);
+  return frame_unwind_got_bytes (this_frame, regnum, buf);
+}
+
+static int
+spu2ppu_sniffer (const struct frame_unwind *self,
+		 struct frame_info *this_frame, void **this_prologue_cache)
+{
+  CORE_ADDR base, func, backchain;
+  gdb_byte buf[4];
+
+  if (gdbarch_bfd_arch_info (target_gdbarch)->arch == bfd_arch_spu)
+    return 0;
+
+  base = get_frame_sp (this_frame);
+  func = get_frame_pc (this_frame);
+  if (target_read_memory (base, buf, 4))
+    return 0;
+  backchain = extract_unsigned_integer (buf, 4);
+
+  if (!backchain)
+    {
+      struct frame_info *fi;
+
+      struct spu2ppu_cache *cache
+	= FRAME_OBSTACK_CALLOC (1, struct spu2ppu_cache);
+
+      cache->frame_id = frame_id_build (base + 16, func);
+
+      for (fi = get_next_frame (this_frame); fi; fi = get_next_frame (fi))
+	if (gdbarch_bfd_arch_info (get_frame_arch (fi))->arch != bfd_arch_spu)
+	  break;
+
+      if (fi)
+	{
+	  cache->regcache = frame_save_as_regcache (fi);
+	  *this_prologue_cache = cache;
+	  return 1;
+	}
+      else
+	{
+	  struct regcache *regcache;
+	  regcache = get_thread_arch_regcache (inferior_ptid, target_gdbarch);
+	  cache->regcache = regcache_dup (regcache);
+	  *this_prologue_cache = cache;
+	  return 1;
+	}
+    }
+
+  return 0;
+}
+
+static void
+spu2ppu_dealloc_cache (struct frame_info *self, void *this_cache)
+{
+  struct spu2ppu_cache *cache = this_cache;
+  regcache_xfree (cache->regcache);
+}
+
+static const struct frame_unwind spu2ppu_unwind = {
+  ARCH_FRAME,
+  spu2ppu_this_id,
+  spu2ppu_prev_register,
+  NULL,
+  spu2ppu_sniffer,
+  spu2ppu_dealloc_cache,
+  spu2ppu_prev_arch,
+};
+
+
 /* Function calling convention.  */
 
 static CORE_ADDR
@@ -2202,6 +2304,9 @@ spu_gdbarch_init (struct gdbarch_info in
   set_gdbarch_skip_prologue (gdbarch, spu_skip_prologue);
   set_gdbarch_in_function_epilogue_p (gdbarch, spu_in_function_epilogue_p);
 
+  /* Cell/B.E. cross-architecture unwinder support.  */
+  frame_unwind_prepend_unwinder (gdbarch, &spu2ppu_unwind);
+
   /* Breakpoints.  */
   set_gdbarch_decr_pc_after_break (gdbarch, 4);
   set_gdbarch_breakpoint_from_pc (gdbarch, spu_breakpoint_from_pc);
-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]