From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 1079 invoked by alias); 24 Mar 2004 19:15:53 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 1065 invoked from network); 24 Mar 2004 19:15:50 -0000 Received: from unknown (HELO mx1.redhat.com) (66.187.233.31) by sources.redhat.com with SMTP; 24 Mar 2004 19:15:50 -0000 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.12.10/8.12.10) with ESMTP id i2OJFoWA032096 for ; Wed, 24 Mar 2004 14:15:50 -0500 Received: from zenia.home.redhat.com (porkchop.devel.redhat.com [172.16.58.2]) by int-mx1.corp.redhat.com (8.11.6/8.11.6) with ESMTP id i2OJFjj04527; Wed, 24 Mar 2004 14:15:46 -0500 To: gdb-patches@sources.redhat.com Subject: RFA: support libthread_db xregset functions From: Jim Blandy Date: Wed, 24 Mar 2004 19:15:00 -0000 Message-ID: User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-SW-Source: 2004-03/txt/msg00571.txt.bz2 The comments in gregset.h have the story. 2003-11-24 Jim Blandy 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