This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
RFA: support libthread_db xregset functions
- From: Jim Blandy <jimb at redhat dot com>
- To: gdb-patches at sources dot redhat dot com
- Date: 24 Mar 2004 14:15:43 -0500
- Subject: RFA: support libthread_db xregset functions
The comments in gregset.h have the story.
2003-11-24 Jim Blandy <jimb@redhat.com>
Allow targets to specify an extended register set, to be passed
through libthread_db via its 'xregs' functions.
* gregset.h (struct xregset_desc): New structure type.
* gdbarch.sh (XREGSET_DESC): New gdbarch member.
* gdbarch.c, gdbarch.h: Regenerated.
* thread-db.c (td_thr_getxregsize_p, td_thr_getxregs_p,
td_thr_setxregs_p): New variables.
(thread_db_load): Initialize them.
(warned_xregs_not_implemented): New variable.
(thread_db_new_objfile): Clear it here.
(thread_db_fetch_registers, thread_db_store_registers): Supply and
fill the xregset, too, if the architecture says it has one, and
libthread_db seems to be able to support it.
* proc-service.c (ps_lgetxregsize, ps_lgetxregs, ps_lsetxregs):
Fill in real implementations of these functions.
Index: gdb/gregset.h
===================================================================
RCS file: /cvs/src/src/gdb/gregset.h,v
retrieving revision 1.7
diff -c -r1.7 gregset.h
*** gdb/gregset.h 8 May 2002 23:29:11 -0000 1.7
--- gdb/gregset.h 24 Mar 2004 19:06:54 -0000
***************
*** 66,69 ****
--- 66,108 ----
extern void fill_fpxregset (gdb_fpxregset_t *fpxregs, int regno);
#endif
+
+ /* The libthread_db interface passes registers in and out using the same
+ structures that appear in core files: gregset_t and fpregset_t.
+ Whether reading or writing:
+ - GDB first moves data out of the regcache into a struct (using
+ fill_gregset or fill_fpregset),
+ - crosses the thread_db boundary (for writing, calls
+ td_thr_setregs; for reading, returns from td_thr_getregs), and
+ - moves the data from the struct back into the regcache (using
+ supply_gregset or supply_fpregset).
+
+ Now, some platforms define even more register sets; for example,
+ members of the PowerPC family supporting the Signal Processing
+ Extension ("SPE") have 'struct speregset'. These need to be passed
+ back and forth across the thread_db boundary. So we need fill_ and
+ supply_ functions for them, too. And we need to know the size of
+ the structure, so we can clear it at the appropriate times.
+
+ An architecture that has such a register set should set its
+ XREGSET_FUNCS gdbarch member to a pointer to one of these
+ structures, which provides the necessary info for working with an
+ extended register set. */
+ struct xregset_desc
+ {
+ /* The name of the register set this set of functions describes ---
+ for debugging, and for error messages. */
+ const char *name;
+
+ /* The size of the register set. */
+ size_t size;
+
+ /* Copy the values of all registers in XREGS into the regcache. */
+ void (*supply) (void *xregs);
+
+ /* Copy the value of register REGNO from the regcache into XREGS.
+ If REGNO is -1, copy all the registers XREGS can hold. */
+ void (*fill) (void *xregs, int regno);
+ };
+
#endif
Index: gdb/gdbarch.sh
===================================================================
RCS file: /cvs/src/src/gdb/gdbarch.sh,v
retrieving revision 1.305
diff -c -r1.305 gdbarch.sh
*** gdb/gdbarch.sh 23 Mar 2004 15:16:41 -0000 1.305
--- gdb/gdbarch.sh 24 Mar 2004 19:06:54 -0000
***************
*** 761,766 ****
--- 761,768 ----
# Fetch the pointer to the ith function argument.
F::FETCH_POINTER_ARGUMENT:CORE_ADDR:fetch_pointer_argument:struct frame_info *frame, int argi, struct type *type:frame, argi, type
+ # See the definition of 'struct xregset_desc' in gregset.h.
+ v::XREGSET_DESC:struct xregset_desc *:xregset_desc:::::0::0:%p:current_gdbarch->xregset_desc
# Return the appropriate register set for a core file section with
# name SECT_NAME and size SECT_SIZE.
M:::const struct regset *:regset_from_core_section:const char *sect_name, size_t sect_size:sect_name, sect_size
Index: gdb/gdbarch.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbarch.c,v
retrieving revision 1.279
diff -c -r1.279 gdbarch.c
*** gdb/gdbarch.c 23 Mar 2004 15:16:39 -0000 1.279
--- gdb/gdbarch.c 24 Mar 2004 19:06:51 -0000
***************
*** 260,265 ****
--- 260,266 ----
gdbarch_address_class_name_to_type_flags_ftype *address_class_name_to_type_flags;
gdbarch_register_reggroup_p_ftype *register_reggroup_p;
gdbarch_fetch_pointer_argument_ftype *fetch_pointer_argument;
+ struct xregset_desc * xregset_desc;
gdbarch_regset_from_core_section_ftype *regset_from_core_section;
};
***************
*** 427,432 ****
--- 428,434 ----
0, /* address_class_name_to_type_flags */
default_register_reggroup_p, /* register_reggroup_p */
0, /* fetch_pointer_argument */
+ 0, /* xregset_desc */
0, /* regset_from_core_section */
/* startup_gdbarch() */
};
***************
*** 735,740 ****
--- 737,743 ----
/* Skip verify of address_class_name_to_type_flags, has predicate */
/* Skip verify of register_reggroup_p, invalid_p == 0 */
/* Skip verify of fetch_pointer_argument, has predicate */
+ /* Skip verify of xregset_desc, invalid_p == 0 */
/* Skip verify of regset_from_core_section, has predicate */
buf = ui_file_xstrdup (log, &dummy);
make_cleanup (xfree, buf);
***************
*** 2446,2451 ****
--- 2449,2462 ----
(long) current_gdbarch->value_to_register
/*VALUE_TO_REGISTER ()*/);
#endif
+ #ifdef XREGSET_DESC
+ fprintf_unfiltered (file,
+ "gdbarch_dump: XREGSET_DESC # %s\n",
+ XSTRING (XREGSET_DESC));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: XREGSET_DESC = %p\n",
+ current_gdbarch->xregset_desc);
+ #endif
if (current_gdbarch->dump_tdep != NULL)
current_gdbarch->dump_tdep (current_gdbarch, file);
}
***************
*** 5365,5370 ****
--- 5376,5398 ----
gdbarch_fetch_pointer_argument_ftype fetch_pointer_argument)
{
gdbarch->fetch_pointer_argument = fetch_pointer_argument;
+ }
+
+ struct xregset_desc *
+ gdbarch_xregset_desc (struct gdbarch *gdbarch)
+ {
+ gdb_assert (gdbarch != NULL);
+ /* Skip verify of xregset_desc, invalid_p == 0 */
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_xregset_desc called\n");
+ return gdbarch->xregset_desc;
+ }
+
+ void
+ set_gdbarch_xregset_desc (struct gdbarch *gdbarch,
+ struct xregset_desc * xregset_desc)
+ {
+ gdbarch->xregset_desc = xregset_desc;
}
int
Index: gdb/gdbarch.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbarch.h,v
retrieving revision 1.241
diff -c -r1.241 gdbarch.h
*** gdb/gdbarch.h 23 Mar 2004 15:16:40 -0000 1.241
--- gdb/gdbarch.h 24 Mar 2004 19:06:52 -0000
***************
*** 2332,2337 ****
--- 2332,2348 ----
#define FETCH_POINTER_ARGUMENT(frame, argi, type) (gdbarch_fetch_pointer_argument (current_gdbarch, frame, argi, type))
#endif
+ /* See the definition of 'struct xregset_desc' in gregset.h. */
+
+ extern struct xregset_desc * gdbarch_xregset_desc (struct gdbarch *gdbarch);
+ extern void set_gdbarch_xregset_desc (struct gdbarch *gdbarch, struct xregset_desc * xregset_desc);
+ #if (GDB_MULTI_ARCH > GDB_MULTI_ARCH_PARTIAL) && defined (XREGSET_DESC)
+ #error "Non multi-arch definition of XREGSET_DESC"
+ #endif
+ #if !defined (XREGSET_DESC)
+ #define XREGSET_DESC (gdbarch_xregset_desc (current_gdbarch))
+ #endif
+
/* Return the appropriate register set for a core file section with
name SECT_NAME and size SECT_SIZE. */
Index: gdb/proc-service.c
===================================================================
RCS file: /cvs/src/src/gdb/proc-service.c,v
retrieving revision 1.7
diff -c -r1.7 proc-service.c
*** gdb/proc-service.c 24 Feb 2002 22:31:19 -0000 1.7
--- gdb/proc-service.c 24 Mar 2004 19:06:55 -0000
***************
*** 131,147 ****
ps_err_e
ps_lgetxregsize (gdb_ps_prochandle_t ph, lwpid_t lwpid, int *xregsize)
{
! /* FIXME: Not supported yet. */
return PS_OK;
}
/* Get the extra state registers of LWP LWPID within the target
process PH and store them in XREGSET. */
-
ps_err_e
ps_lgetxregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, caddr_t xregset)
{
! /* FIXME: Not supported yet. */
return PS_OK;
}
--- 131,163 ----
ps_err_e
ps_lgetxregsize (gdb_ps_prochandle_t ph, lwpid_t lwpid, int *xregsize)
{
! if (! XREGSET_DESC)
! /* This architecture has no extra registers. */
! return PS_ERR;
!
! *xregsize = XREGSET_DESC->size;
return PS_OK;
}
+
/* Get the extra state registers of LWP LWPID within the target
process PH and store them in XREGSET. */
ps_err_e
ps_lgetxregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, caddr_t xregset)
{
! struct cleanup *old_chain = save_inferior_ptid ();
! struct xregset_desc *desc = XREGSET_DESC;
!
! if (! desc)
! /* This target has no extra registers. */
! return PS_OK;
!
! inferior_ptid = BUILD_LWP (lwpid, ph->pid);
!
! target_fetch_registers (-1);
! desc->fill ((void *) xregset, -1);
!
! do_cleanups (old_chain);
return PS_OK;
}
***************
*** 151,157 ****
ps_err_e
ps_lsetxregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, caddr_t xregset)
{
! /* FIXME: Not supported yet. */
return PS_OK;
}
--- 167,186 ----
ps_err_e
ps_lsetxregs (gdb_ps_prochandle_t ph, lwpid_t lwpid, caddr_t xregset)
{
! struct cleanup *old_chain = save_inferior_ptid ();
! struct xregset_desc *desc = XREGSET_DESC;
!
! if (! desc)
! /* This architecture has no extended registers. */
! return PS_OK;
!
! inferior_ptid = BUILD_LWP (lwpid, ph->pid);
!
! /* FIXME: We should really make supply_gregset const-correct. */
! desc->supply ((void *) xregset);
! target_store_registers (-1);
!
! do_cleanups (old_chain);
return PS_OK;
}
Index: gdb/thread-db.c
===================================================================
RCS file: /cvs/src/src/gdb/thread-db.c,v
retrieving revision 1.37
diff -c -r1.37 thread-db.c
*** gdb/thread-db.c 29 Feb 2004 02:39:47 -0000 1.37
--- gdb/thread-db.c 24 Mar 2004 19:06:56 -0000
***************
*** 101,114 ****
--- 101,120 ----
static td_err_e (*td_thr_validate_p) (const td_thrhandle_t *th);
static td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th,
td_thrinfo_t *infop);
+ static td_err_e (*td_thr_getxregsize_p) (const td_thrhandle_t *__th,
+ int *__sizep);
static td_err_e (*td_thr_getfpregs_p) (const td_thrhandle_t *th,
gdb_prfpregset_t *regset);
static td_err_e (*td_thr_getgregs_p) (const td_thrhandle_t *th,
prgregset_t gregs);
+ static td_err_e (*td_thr_getxregs_p) (const td_thrhandle_t *__th,
+ void *__xregs);
static td_err_e (*td_thr_setfpregs_p) (const td_thrhandle_t *th,
const gdb_prfpregset_t *fpregs);
static td_err_e (*td_thr_setgregs_p) (const td_thrhandle_t *th,
prgregset_t gregs);
+ static td_err_e (*td_thr_setxregs_p) (const td_thrhandle_t *__th,
+ const void *__addr);
static td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th,
int event);
***************
*** 126,131 ****
--- 132,145 ----
/* Location of the thread death event breakpoint. */
static CORE_ADDR td_death_bp_addr;
+ /* On some architectures, there are additional regs beyond the gregset
+ and fpregset. The libthread_db interface has functions to access
+ these, but on some versions of libthread_db they are not
+ implemented. We want to warn the user about this, but not treat it
+ as a fatal error, since you can still access the other
+ registers. */
+ static int warned_xregs_not_implemented;
+
/* Prototypes for local functions. */
static void thread_db_find_new_threads (void);
static void attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
***************
*** 431,436 ****
--- 445,454 ----
if (td_thr_get_info_p == NULL)
return 0;
+ td_thr_getxregsize_p = verbose_dlsym (handle, "td_thr_getxregsize");
+ if (td_thr_getxregsize_p == NULL)
+ return 0;
+
td_thr_getfpregs_p = verbose_dlsym (handle, "td_thr_getfpregs");
if (td_thr_getfpregs_p == NULL)
return 0;
***************
*** 439,444 ****
--- 457,466 ----
if (td_thr_getgregs_p == NULL)
return 0;
+ td_thr_getxregs_p = verbose_dlsym (handle, "td_thr_getxregs");
+ if (td_thr_getxregs_p == NULL)
+ return 0;
+
td_thr_setfpregs_p = verbose_dlsym (handle, "td_thr_setfpregs");
if (td_thr_setfpregs_p == NULL)
return 0;
***************
*** 447,452 ****
--- 469,478 ----
if (td_thr_setgregs_p == NULL)
return 0;
+ td_thr_setxregs_p = verbose_dlsym (handle, "td_thr_setxregs");
+ if (td_thr_setxregs_p == NULL)
+ return 0;
+
/* Initialize the library. */
err = td_init_p ();
if (err != TD_OK)
***************
*** 684,689 ****
--- 710,720 ----
break;
}
+ /* If the gdbarch says we have an extended register set, but we are
+ unable to access them via libthread_db, we want to issue one
+ warning each time we active libthread_db. */
+ warned_xregs_not_implemented = 0;
+
quit:
if (target_new_objfile_chain)
target_new_objfile_chain (objfile);
***************
*** 933,938 ****
--- 964,972 ----
struct thread_info *thread_info;
prgregset_t gregset;
gdb_prfpregset_t fpregset;
+ void *xregset;
+ int fetched_xregs = 0;
+ struct xregset_desc *xregset_desc = XREGSET_DESC;
td_err_e err;
if (!is_thread (inferior_ptid))
***************
*** 945,950 ****
--- 979,989 ----
thread_info = find_thread_pid (inferior_ptid);
thread_db_map_id2thr (thread_info, 1);
+ if (xregset_desc)
+ xregset = alloca (xregset_desc->size);
+ else
+ xregset = 0;
+
err = td_thr_getgregs_p (&thread_info->private->th, gregset);
if (err != TD_OK)
error ("Cannot fetch general-purpose registers for thread %ld: %s",
***************
*** 955,965 ****
--- 994,1033 ----
error ("Cannot get floating-point registers for thread %ld: %s",
(long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
+ if (xregset_desc)
+ {
+ err = td_thr_getxregs_p (&thread_info->private->th, xregset);
+ switch (err)
+ {
+ case TD_OK:
+ fetched_xregs = 1;
+ break;
+
+ case TD_NOXREGS:
+ if (! warned_xregs_not_implemented)
+ {
+ warning ("thread debugging library is too old to access"
+ " %s registers.",
+ xregset_desc->name);
+ warned_xregs_not_implemented = 1;
+ }
+ break;
+
+ default:
+ error ("Cannot get %s registers for thread %ld: %s",
+ xregset_desc->name,
+ (long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
+ }
+ }
+
/* Note that we must call supply_gregset after calling the thread_db
routines because the thread_db routines call ps_lgetgregs and
friends which clobber GDB's register cache. */
supply_gregset ((gdb_gregset_t *) gregset);
supply_fpregset (&fpregset);
+
+ if (fetched_xregs)
+ xregset_desc->supply (xregset);
}
static void
***************
*** 967,972 ****
--- 1035,1042 ----
{
prgregset_t gregset;
gdb_prfpregset_t fpregset;
+ struct xregset_desc *xregset_desc = XREGSET_DESC;
+ void *xregset;
td_err_e err;
struct thread_info *thread_info;
***************
*** 991,996 ****
--- 1061,1073 ----
fill_gregset ((gdb_gregset_t *) gregset, -1);
fill_fpregset (&fpregset, -1);
+ if (xregset_desc)
+ {
+ xregset = alloca (xregset_desc->size);
+ xregset_desc->fill (xregset, -1);
+ }
+ else
+ xregset = 0;
err = td_thr_setgregs_p (&thread_info->private->th, gregset);
if (err != TD_OK)
***************
*** 1000,1005 ****
--- 1077,1100 ----
if (err != TD_OK)
error ("Cannot store floating-point registers for thread %ld: %s",
(long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
+ if (xregset_desc)
+ {
+ err = td_thr_setxregs_p (&thread_info->private->th, xregset);
+ if (err == TD_NOXREGS)
+ {
+ if (! warned_xregs_not_implemented)
+ {
+ warning ("thread debugging library is too old to access"
+ " %s registers.",
+ xregset_desc->name);
+ warned_xregs_not_implemented = 1;
+ }
+ }
+ else if (err != TD_OK)
+ error ("Cannot store %s registers for thread %ld: %s",
+ xregset_desc->name,
+ (long) GET_THREAD (inferior_ptid), thread_db_err_str (err));
+ }
}
static void