diff options
author | Andrew Cagney <cagney@redhat.com> | 2002-02-24 04:31:13 +0000 |
---|---|---|
committer | Andrew Cagney <cagney@redhat.com> | 2002-02-24 04:31:13 +0000 |
commit | a3007b6fd4dd19e21fa61483ba00d5f8cb732ca9 (patch) | |
tree | 54b9e049c34e2a6761aa4b64707d55fab076ef01 /gdb | |
parent | 50a9e2f10c9eff04f5a218a9be851df92109ab29 (diff) | |
download | gdb-a3007b6fd4dd19e21fa61483ba00d5f8cb732ca9.zip gdb-a3007b6fd4dd19e21fa61483ba00d5f8cb732ca9.tar.gz gdb-a3007b6fd4dd19e21fa61483ba00d5f8cb732ca9.tar.bz2 |
Zap lin-thread.c
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 5 | ||||
-rw-r--r-- | gdb/configure.in | 2 | ||||
-rw-r--r-- | gdb/lin-thread.c | 2044 |
3 files changed, 6 insertions, 2045 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 71268f3..330f28b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,10 @@ 2002-02-23 Andrew Cagney <ac131313@redhat.com> + * lin-thread.c: Delete file. + * configure.in (gdb_cv_struct_reg_r_gs): Update comment to refer + to gdb_proc_service.h. + * configure: Re-generate. + * ocd.c (ocd_open): Do not try to open the "ocd" device. * serial.c (serial_open): Delete check for "ocd". Fix PR gdb/349. diff --git a/gdb/configure.in b/gdb/configure.in index cb9e59b..382ddb6 100644 --- a/gdb/configure.in +++ b/gdb/configure.in @@ -307,7 +307,7 @@ if test "$ac_cv_header_sys_procfs_h" = yes; then dnl For Linux/i386, glibc 2.1.3 was released with a bogus dnl prfpregset_t type (it's a typedef for the pointer to a struct dnl instead of the struct itself). We detect this here, and work - dnl around it in lin-thread.c. + dnl around it in gdb_proc_service.h. if test $bfd_cv_have_sys_procfs_type_prfpregset_t = yes; then AC_MSG_CHECKING(whether prfpregset_t type is broken) diff --git a/gdb/lin-thread.c b/gdb/lin-thread.c deleted file mode 100644 index a14af7b..0000000 --- a/gdb/lin-thread.c +++ /dev/null @@ -1,2044 +0,0 @@ -/* Multi-threaded debugging support for the thread_db interface, - used on operating systems such as Solaris and Linux. - Copyright 1999, 2000, 2001 Free Software Foundation, Inc. - - 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. */ - -/* This module implements a thread_stratum target that sits on top of - a normal process_stratum target (such as procfs or ptrace). The - process_stratum target must install this thread_stratum target when - it detects the presence of the thread_db shared library. - - This module will then use the thread_db API to add thread-awareness - to the functionality provided by the process_stratum target (or in - some cases, to add user-level thread awareness on top of the - kernel-level thread awareness that is already provided by the - process_stratum target). - - Solaris threads (for instance) are a multi-level thread implementation; - the kernel provides a Light Weight Process (LWP) which the procfs - process_stratum module is aware of. This module must then mediate - the relationship between kernel LWP threads and user (eg. posix) - threads. - - Linux threads are likely to be different -- but the thread_db - library API should make the difference largely transparent to GDB. - - */ - -/* The thread_db API provides a number of functions that give the caller - access to the inner workings of the child process's thread library. - We will be using the following (others may be added): - - td_thr_validate Confirm valid "live" thread - td_thr_get_info Get info about a thread - td_thr_getgregs Get thread's general registers - td_thr_getfpregs Get thread's floating point registers - td_thr_setgregs Set thread's general registers - td_thr_setfpregs Set thread's floating point registers - td_ta_map_id2thr Get thread handle from thread id - td_ta_map_lwp2thr Get thread handle from LWP id - td_ta_thr_iter Iterate over all threads (with callback) - - In return, the debugger has to provide certain services to the - thread_db library. Some of these aren't actually required to do - anything in practice. For instance, the thread_db expects to be - able to stop the child process and start it again: but in our - context, the child process will always be stopped already when we - invoke the thread_db library, so the functions that we provide for - the library to stop and start the child process are no-ops. - - Here is the list of functions which we export to the thread_db - library, divided into no-op functions vs. functions that actually - have to do something: - - No-op functions: - - ps_pstop Stop the child process - ps_pcontinue Continue the child process - ps_lstop Stop a specific LWP (kernel thread) - ps_lcontinue Continue an LWP - ps_lgetxregsize Get size of LWP's xregs (sparc) - ps_lgetxregs Get LWP's xregs (sparc) - ps_lsetxregs Set LWP's xregs (sparc) - - Functions that have to do useful work: - - ps_pglobal_lookup Get the address of a global symbol - ps_pdread Read memory, data segment - ps_ptread Read memory, text segment - ps_pdwrite Write memory, data segment - ps_ptwrite Write memory, text segment - ps_lgetregs Get LWP's general registers - ps_lgetfpregs Get LWP's floating point registers - ps_lsetregs Set LWP's general registers - ps_lsetfpregs Set LWP's floating point registers - ps_lgetLDT Get LWP's Local Descriptor Table (x86) - - Thus, if we ask the thread_db library to give us the general registers - for user thread X, thread_db may figure out that user thread X is - actually mapped onto kernel thread Y. Thread_db does not know how - to obtain the registers for kernel thread Y, but GDB does, so thread_db - turns the request right back to us via the ps_lgetregs callback. */ - -#include "defs.h" -#include "gdbthread.h" -#include "target.h" -#include "inferior.h" -#include "gdbcmd.h" -#include "regcache.h" - -#include "gdb_wait.h" - -#include <time.h> - -#if defined(USE_PROC_FS) || defined(HAVE_GREGSET_T) -#include <sys/procfs.h> -#endif - -#include "gdb_proc_service.h" - -#if defined HAVE_STDINT_H /* Pre-5.2 systems don't have this header */ -#if defined (HAVE_THREAD_DB_H) -#include <thread_db.h> /* defines outgoing API (td_thr_* calls) */ -#else -#include "gdb_thread_db.h" -#endif - -#include <dlfcn.h> /* dynamic library interface */ - -/* Prototypes for supply_gregset etc. */ -#include "gregset.h" - -/* Macros for superimposing PID and TID into inferior_ptid. */ -#define GET_PID(ptid) ptid_get_pid (ptid) -#define GET_LWP(ptid) ptid_get_lwp (ptid) -#define GET_THREAD(ptid) ptid_get_tid (ptid) - -#define is_lwp(ptid) (GET_LWP (ptid) != 0) -#define is_thread(ptid) (GET_THREAD (ptid) != 0) - -#define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0) -#define BUILD_THREAD(tid, pid) ptid_build (pid, 0, tid) - -/* From linux-thread.c. FIXME: These should go in a separate header - file, but I'm told that the life expectancy of lin-thread.c and - linux-thread.c isn't very long... */ - -extern int linux_child_wait (int, int *, int *); -extern void check_all_signal_numbers (void); -extern void linuxthreads_discard_global_state (void); -extern void attach_thread (int); - -/* - * target_beneath is a pointer to the target_ops underlying this one. - */ - -static struct target_ops *target_beneath; - - -/* - * target vector defined in this module: - */ - -static struct target_ops thread_db_ops; - -/* - * Typedefs required to resolve differences between the thread_db - * and proc_service API defined on different versions of Solaris: - */ - -#if defined(PROC_SERVICE_IS_OLD) -typedef const struct ps_prochandle *gdb_ps_prochandle_t; -typedef char *gdb_ps_read_buf_t; -typedef char *gdb_ps_write_buf_t; -typedef int gdb_ps_size_t; -#else -typedef struct ps_prochandle *gdb_ps_prochandle_t; -typedef void *gdb_ps_read_buf_t; -typedef const void *gdb_ps_write_buf_t; -typedef size_t gdb_ps_size_t; -#endif - -/* - * proc_service callback functions, called by thread_db. - */ - -ps_err_e -ps_pstop (gdb_ps_prochandle_t ph) /* Process stop */ -{ - return PS_OK; -} - -ps_err_e -ps_pcontinue (gdb_ps_prochandle_t ph) /* Process continue */ -{ - return PS_OK; -} - -ps_err_e -ps_lstop (gdb_ps_prochandle_t ph, /* LWP stop */ - lwpid_t lwpid) -{ - return PS_OK; -} - -ps_err_e -ps_lcontinue (gdb_ps_prochandle_t ph, /* LWP continue */ - lwpid_t lwpid) -{ - return PS_OK; -} - -ps_err_e -ps_lgetxregsize (gdb_ps_prochandle_t ph, /* Get XREG size */ - lwpid_t lwpid, - int *xregsize) -{ - return PS_OK; -} - -ps_err_e -ps_lgetxregs (gdb_ps_prochandle_t ph, /* Get XREGS */ - lwpid_t lwpid, - caddr_t xregset) -{ - return PS_OK; -} - -ps_err_e -ps_lsetxregs (gdb_ps_prochandle_t ph, /* Set XREGS */ - lwpid_t lwpid, - caddr_t xregset) -{ - return PS_OK; -} - -void -ps_plog (const char *fmt, ...) -{ - va_list args; - - va_start (args, fmt); - vfprintf_filtered (gdb_stderr, fmt, args); -} - -/* Look up a symbol in GDB's global symbol table. - Return the symbol's address. - FIXME: it would be more correct to look up the symbol in the context - of the LD_OBJECT_NAME provided. However we're probably fairly safe - as long as there aren't name conflicts with other libraries. */ - -ps_err_e -ps_pglobal_lookup (gdb_ps_prochandle_t ph, - const char *ld_object_name, /* the library name */ - const char *ld_symbol_name, /* the symbol name */ - paddr_t *ld_symbol_addr) /* return the symbol addr */ -{ - struct minimal_symbol *ms; - - ms = lookup_minimal_symbol (ld_symbol_name, NULL, NULL); - - if (!ms) - return PS_NOSYM; - - *ld_symbol_addr = SYMBOL_VALUE_ADDRESS (ms); - - return PS_OK; -} - -/* Worker function for all memory reads and writes: */ -static ps_err_e rw_common (const struct ps_prochandle *ph, - paddr_t addr, - char *buf, - int size, - int write_p); - -/* target_xfer_memory direction consts */ -enum {PS_READ = 0, PS_WRITE = 1}; - -ps_err_e -ps_pdread (gdb_ps_prochandle_t ph, /* read from data segment */ - paddr_t addr, - gdb_ps_read_buf_t buf, - gdb_ps_size_t size) -{ - return rw_common (ph, addr, buf, size, PS_READ); -} - -ps_err_e -ps_pdwrite (gdb_ps_prochandle_t ph, /* write to data segment */ - paddr_t addr, - gdb_ps_write_buf_t buf, - gdb_ps_size_t size) -{ - return rw_common (ph, addr, (char *) buf, size, PS_WRITE); -} - -ps_err_e -ps_ptread (gdb_ps_prochandle_t ph, /* read from text segment */ - paddr_t addr, - gdb_ps_read_buf_t buf, - gdb_ps_size_t size) -{ - return rw_common (ph, addr, buf, size, PS_READ); -} - -ps_err_e -ps_ptwrite (gdb_ps_prochandle_t ph, /* write to text segment */ - paddr_t addr, - gdb_ps_write_buf_t buf, - gdb_ps_size_t size) -{ - return rw_common (ph, addr, (char *) buf, size, PS_WRITE); -} - -static char *thr_err_string (td_err_e); -static char *thr_state_string (td_thr_state_e); - -struct ps_prochandle main_prochandle; -td_thragent_t * main_threadagent; - -/* - * Common proc_service routine for reading and writing memory. - */ - -/* FIXME: once we've munged the inferior_ptid, why can't we - simply call target_read/write_memory and return? */ - -static ps_err_e -rw_common (const struct ps_prochandle *ph, - paddr_t addr, - char *buf, - int size, - int write_p) -{ - struct cleanup *old_chain = save_inferior_ptid (); - int to_do = size; - int done = 0; - - inferior_ptid = pid_to_ptid (main_prochandle.pid); - - while (to_do > 0) - { - done = current_target.to_xfer_memory (addr, buf, size, write_p, - ¤t_target); - if (done <= 0) - { - if (write_p == PS_READ) - print_sys_errmsg ("rw_common (): read", errno); - else - print_sys_errmsg ("rw_common (): write", errno); - - return PS_ERR; - } - to_do -= done; - buf += done; - } - do_cleanups (old_chain); - return PS_OK; -} - -/* Cleanup functions used by the register callbacks - (which have to manipulate the global inferior_ptid). */ - -ps_err_e -ps_lgetregs (gdb_ps_prochandle_t ph, /* Get LWP general regs */ - lwpid_t lwpid, - prgregset_t gregset) -{ - struct cleanup *old_chain = save_inferior_ptid (); - - inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid); - current_target.to_fetch_registers (-1); - - fill_gregset ((gdb_gregset_t *) gregset, -1); - do_cleanups (old_chain); - - return PS_OK; -} - -ps_err_e -ps_lsetregs (gdb_ps_prochandle_t ph, /* Set LWP general regs */ - lwpid_t lwpid, - const prgregset_t gregset) -{ - struct cleanup *old_chain = save_inferior_ptid (); - - inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid); - supply_gregset ((gdb_gregset_t *) gregset); - current_target.to_store_registers (-1); - do_cleanups (old_chain); - return PS_OK; -} - -ps_err_e -ps_lgetfpregs (gdb_ps_prochandle_t ph, /* Get LWP float regs */ - lwpid_t lwpid, - gdb_prfpregset_t *fpregset) -{ - struct cleanup *old_chain = save_inferior_ptid (); - - inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid); - current_target.to_fetch_registers (-1); - fill_fpregset (fpregset, -1); - do_cleanups (old_chain); - return PS_OK; -} - -ps_err_e -ps_lsetfpregs (gdb_ps_prochandle_t ph, /* Set LWP float regs */ - lwpid_t lwpid, - const gdb_prfpregset_t *fpregset) -{ - struct cleanup *old_chain = save_inferior_ptid (); - - inferior_ptid = BUILD_LWP (lwpid, main_prochandle.pid); - supply_fpregset (fpregset); - current_target.to_store_registers (-1); - do_cleanups (old_chain); - return PS_OK; -} - -/* - * ps_getpid - * - * return the main pid for the child process - * (special for Linux -- not used on Solaris) - */ - -pid_t -ps_getpid (gdb_ps_prochandle_t ph) -{ - return ph->pid; -} - -#ifdef TM_I386SOL2_H - -/* Reads the local descriptor table of a LWP. */ - -ps_err_e -ps_lgetLDT (gdb_ps_prochandle_t ph, lwpid_t lwpid, - struct ssd *pldt) -{ - /* NOTE: only used on Solaris, therefore OK to refer to procfs.c */ - extern struct ssd *procfs_find_LDT_entry (int); - struct ssd *ret; - - ret = procfs_find_LDT_entry (BUILD_LWP (lwpid, - PIDGET (main_prochandle.pid))); - if (ret) - { - memcpy (pldt, ret, sizeof (struct ssd)); - return PS_OK; - } - else /* LDT not found. */ - return PS_ERR; -} -#endif /* TM_I386SOL2_H */ - -/* - * Pointers to thread_db functions: - * - * These are a dynamic library mechanism. - * The dlfcn.h interface will be used to initialize these - * so that they point to the appropriate functions in the - * thread_db dynamic library. This is done dynamically - * so that GDB can still run on systems that lack thread_db. - */ - -static td_err_e (*p_td_init) (void); - -static td_err_e (*p_td_ta_new) (const struct ps_prochandle *ph_p, - td_thragent_t **ta_pp); - -static td_err_e (*p_td_ta_delete) (td_thragent_t *ta_p); - -static td_err_e (*p_td_ta_get_nthreads) (const td_thragent_t *ta_p, - int *nthread_p); - - -static td_err_e (*p_td_ta_thr_iter) (const td_thragent_t *ta_p, - td_thr_iter_f *cb, - void *cbdata_p, - td_thr_state_e state, - int ti_pri, - sigset_t *ti_sigmask_p, - unsigned ti_user_flags); - -static td_err_e (*p_td_ta_event_addr) (const td_thragent_t *ta_p, - u_long event, - td_notify_t *notify_p); - -static td_err_e (*p_td_ta_event_getmsg) (const td_thragent_t *ta_p, - td_event_msg_t *msg); - -static td_err_e (*p_td_ta_set_event) (const td_thragent_t *ta_p, - td_thr_events_t *events); - -static td_err_e (*p_td_thr_validate) (const td_thrhandle_t *th_p); - -static td_err_e (*p_td_thr_event_enable) (const td_thrhandle_t *th_p, - int on_off); - -static td_err_e (*p_td_thr_get_info) (const td_thrhandle_t *th_p, - td_thrinfo_t *ti_p); - -static td_err_e (*p_td_thr_getgregs) (const td_thrhandle_t *th_p, - prgregset_t regset); - -static td_err_e (*p_td_thr_setgregs) (const td_thrhandle_t *th_p, - const prgregset_t regset); - -static td_err_e (*p_td_thr_getfpregs) (const td_thrhandle_t *th_p, - gdb_prfpregset_t *fpregset); - -static td_err_e (*p_td_thr_setfpregs) (const td_thrhandle_t *th_p, - const gdb_prfpregset_t *fpregset); - -static td_err_e (*p_td_ta_map_id2thr) (const td_thragent_t *ta_p, - thread_t tid, - td_thrhandle_t *th_p); - -static td_err_e (*p_td_ta_map_lwp2thr) (const td_thragent_t *ta_p, - lwpid_t lwpid, - td_thrhandle_t *th_p); - -/* - * API and target vector initialization function: thread_db_initialize. - * - * NOTE: this function is deliberately NOT named with the GDB convention - * of module initializer function names that begin with "_initialize". - * This module is NOT intended to be auto-initialized at GDB startup. - * Rather, it will only be initialized when a multi-threaded child - * process is detected. - * - */ - -/* - * Initializer for thread_db library interface. - * This function does the dynamic library stuff (dlopen, dlsym), - * and then calls the thread_db library's one-time initializer - * function (td_init). If everything succeeds, this function - * returns true; otherwise it returns false, and this module - * cannot be used. - */ - -static int -init_thread_db_library (void) -{ - void *dlhandle; - td_err_e ret; - - /* Open a handle to the "thread_db" dynamic library. */ - if ((dlhandle = dlopen ("libthread_db.so.1", RTLD_NOW)) == NULL) - return 0; /* fail */ - - /* Initialize pointers to the dynamic library functions we will use. - * Note that we are not calling the functions here -- we are only - * establishing pointers to them. - */ - - /* td_init: initialize thread_db library. */ - if ((p_td_init = dlsym (dlhandle, "td_init")) == NULL) - return 0; /* fail */ - /* td_ta_new: register a target process with thread_db. */ - if ((p_td_ta_new = dlsym (dlhandle, "td_ta_new")) == NULL) - return 0; /* fail */ - /* td_ta_delete: un-register a target process with thread_db. */ - if ((p_td_ta_delete = dlsym (dlhandle, "td_ta_delete")) == NULL) - return 0; /* fail */ - - /* td_ta_map_id2thr: get thread handle from thread id. */ - if ((p_td_ta_map_id2thr = dlsym (dlhandle, "td_ta_map_id2thr")) == NULL) - return 0; /* fail */ - /* td_ta_map_lwp2thr: get thread handle from lwp id. */ - if ((p_td_ta_map_lwp2thr = dlsym (dlhandle, "td_ta_map_lwp2thr")) == NULL) - return 0; /* fail */ - /* td_ta_get_nthreads: get number of threads in target process. */ - if ((p_td_ta_get_nthreads = dlsym (dlhandle, "td_ta_get_nthreads")) == NULL) - return 0; /* fail */ - /* td_ta_thr_iter: iterate over all thread handles. */ - if ((p_td_ta_thr_iter = dlsym (dlhandle, "td_ta_thr_iter")) == NULL) - return 0; /* fail */ - - /* td_thr_validate: make sure a thread handle is real and alive. */ - if ((p_td_thr_validate = dlsym (dlhandle, "td_thr_validate")) == NULL) - return 0; /* fail */ - /* td_thr_get_info: get a bunch of info about a thread. */ - if ((p_td_thr_get_info = dlsym (dlhandle, "td_thr_get_info")) == NULL) - return 0; /* fail */ - /* td_thr_getgregs: get general registers for thread. */ - if ((p_td_thr_getgregs = dlsym (dlhandle, "td_thr_getgregs")) == NULL) - return 0; /* fail */ - /* td_thr_setgregs: set general registers for thread. */ - if ((p_td_thr_setgregs = dlsym (dlhandle, "td_thr_setgregs")) == NULL) - return 0; /* fail */ - /* td_thr_getfpregs: get floating point registers for thread. */ - if ((p_td_thr_getfpregs = dlsym (dlhandle, "td_thr_getfpregs")) == NULL) - return 0; /* fail */ - /* td_thr_setfpregs: set floating point registers for thread. */ - if ((p_td_thr_setfpregs = dlsym (dlhandle, "td_thr_setfpregs")) == NULL) - return 0; /* fail */ - - ret = p_td_init (); - if (ret != TD_OK) - { - warning ("init_thread_db: td_init: %s", thr_err_string (ret)); - return 0; - } - - /* Optional functions: - We can still debug even if the following functions are not found. */ - - /* td_ta_event_addr: get the breakpoint address for specified event. */ - p_td_ta_event_addr = dlsym (dlhandle, "td_ta_event_addr"); - - /* td_ta_event_getmsg: get the next event message for the process. */ - p_td_ta_event_getmsg = dlsym (dlhandle, "td_ta_event_getmsg"); - - /* td_ta_set_event: request notification of an event. */ - p_td_ta_set_event = dlsym (dlhandle, "td_ta_set_event"); - - /* td_thr_event_enable: enable event reporting in a thread. */ - p_td_thr_event_enable = dlsym (dlhandle, "td_thr_event_enable"); - - return 1; /* success */ -} - -/* - * Local utility functions: - */ - -/* - - LOCAL FUNCTION - - thr_err_string - Convert a thread_db error code to a string - - SYNOPSIS - - char * thr_err_string (errcode) - - DESCRIPTION - - Return a string description of the thread_db errcode. If errcode - is unknown, then return an <unknown> message. - - */ - -static char * -thr_err_string (td_err_e errcode) -{ - static char buf[50]; - - switch (errcode) { - case TD_OK: return "generic 'call succeeded'"; - case TD_ERR: return "generic error"; - case TD_NOTHR: return "no thread to satisfy query"; - case TD_NOSV: return "no sync handle to satisfy query"; - case TD_NOLWP: return "no lwp to satisfy query"; - case TD_BADPH: return "invalid process handle"; - case TD_BADTH: return "invalid thread handle"; - case TD_BADSH: return "invalid synchronization handle"; - case TD_BADTA: return "invalid thread agent"; - case TD_BADKEY: return "invalid key"; - case TD_NOMSG: return "no event message for getmsg"; - case TD_NOFPREGS: return "FPU register set not available"; - case TD_NOLIBTHREAD: return "application not linked with libthread"; - case TD_NOEVENT: return "requested event is not supported"; - case TD_NOCAPAB: return "capability not available"; - case TD_DBERR: return "debugger service failed"; - case TD_NOAPLIC: return "operation not applicable to"; - case TD_NOTSD: return "no thread-specific data for this thread"; - case TD_MALLOC: return "malloc failed"; - case TD_PARTIALREG: return "only part of register set was written/read"; - case TD_NOXREGS: return "X register set not available for this thread"; - default: - sprintf (buf, "unknown thread_db error '%d'", errcode); - return buf; - } -} - -/* - - LOCAL FUNCTION - - thr_state_string - Convert a thread_db state code to a string - - SYNOPSIS - - char *thr_state_string (statecode) - - DESCRIPTION - - Return the thread_db state string associated with statecode. - If statecode is unknown, then return an <unknown> message. - - */ - -static char * -thr_state_string (td_thr_state_e statecode) -{ - static char buf[50]; - - switch (statecode) { - case TD_THR_STOPPED: return "stopped by debugger"; - case TD_THR_RUN: return "runnable"; - case TD_THR_ACTIVE: return "active"; - case TD_THR_ZOMBIE: return "zombie"; - case TD_THR_SLEEP: return "sleeping"; - case TD_THR_STOPPED_ASLEEP: return "stopped by debugger AND blocked"; - default: - sprintf (buf, "unknown thread_db state %d", statecode); - return buf; - } -} - -/* - * Local thread/event list. - * This data structure will be used to hold a list of threads and - * pending/deliverable events. - */ - -typedef struct THREADINFO { - thread_t tid; /* thread ID */ - pid_t lid; /* process/lwp ID */ - td_thr_state_e state; /* thread state (a la thread_db) */ - td_thr_type_e type; /* thread type (a la thread_db) */ - int pending; /* true if holding a pending event */ - int status; /* wait status of any interesting event */ -} threadinfo; - -threadinfo * threadlist; -int threadlist_max = 0; /* current size of table */ -int threadlist_top = 0; /* number of threads now in table */ -#define THREADLIST_ALLOC 100 /* chunk size by which to expand table */ - -static threadinfo * -insert_thread (int tid, int lid, td_thr_state_e state, td_thr_type_e type) -{ - if (threadlist_top >= threadlist_max) - { - threadlist_max += THREADLIST_ALLOC; - threadlist = xrealloc (threadlist, - threadlist_max * sizeof (threadinfo)); - if (threadlist == NULL) - return NULL; - } - threadlist[threadlist_top].tid = tid; - threadlist[threadlist_top].lid = lid; - threadlist[threadlist_top].state = state; - threadlist[threadlist_top].type = type; - threadlist[threadlist_top].pending = 0; - threadlist[threadlist_top].status = 0; - - return &threadlist[threadlist_top++]; -} - -static void -empty_threadlist (void) -{ - threadlist_top = 0; -} - -static threadinfo * -next_pending_event (void) -{ - int i; - - for (i = 0; i < threadlist_top; i++) - if (threadlist[i].pending) - return &threadlist[i]; - - return NULL; -} - -static void -threadlist_iter (int (*func) (), void *data, td_thr_state_e state, - td_thr_type_e type) -{ - int i; - - for (i = 0; i < threadlist_top; i++) - if ((state == TD_THR_ANY_STATE || state == threadlist[i].state) && - (type == TD_THR_ANY_TYPE || type == threadlist[i].type)) - if ((*func) (&threadlist[i], data) != 0) - break; - - return; -} - -/* - * Global state - * - * Here we keep state information all collected in one place. - */ - -/* This flag is set when we activate, so that we don't do it twice. - Defined in linux-thread.c and used for inter-target syncronization. */ -extern int using_thread_db; - -/* The process id for which we've stopped. - * This is only set when we actually stop all threads. - * Otherwise it's zero. - */ -static int event_pid; - -/* - * The process id for a new thread to which we've just attached. - * This process needs special handling at resume time. - */ -static int attach_pid; - - -/* - * thread_db event handling: - * - * The mechanism for event notification via the thread_db API. - * These events are implemented as breakpoints. The thread_db - * library gives us an address where we can set a breakpoint. - * When the breakpoint is hit, it represents an event of interest - * such as: - * Thread creation - * Thread death - * Thread reap - */ - -/* Location of the thread creation event breakpoint. The code at this - location in the child process will be called by the pthread library - whenever a new thread is created. By setting a special breakpoint - at this location, GDB can detect when a new thread is created. We - obtain this location via the td_ta_event_addr call. */ - -static CORE_ADDR thread_creation_bkpt_address; - -/* Location of the thread death event breakpoint. The code at this - location in the child process will be called by the pthread library - whenever a thread is destroyed. By setting a special breakpoint at - this location, GDB can detect when a new thread is created. We - obtain this location via the td_ta_event_addr call. */ - -static CORE_ADDR thread_death_bkpt_address; - -/* This function handles the global parts of enabling thread events. - The thread-specific enabling is handled per-thread elsewhere. */ - -static void -enable_thread_event_reporting (td_thragent_t *ta) -{ - td_thr_events_t events; - td_notify_t notify; - CORE_ADDR addr; - - if (p_td_ta_set_event == NULL || - p_td_ta_event_addr == NULL || - p_td_ta_event_getmsg == NULL || - p_td_thr_event_enable == NULL) - return; /* can't do thread event reporting without these funcs */ - - /* set process wide mask saying which events we are interested in */ - td_event_emptyset (&events); - td_event_addset (&events, TD_CREATE); - td_event_addset (&events, TD_DEATH); - - if (p_td_ta_set_event (ta, &events) != TD_OK) - { - warning ("unable to set global thread event mask"); - return; - } - - /* Delete previous thread event breakpoints, if any. */ - remove_thread_event_breakpoints (); - - /* create breakpoints -- thread creation and death */ - /* thread creation */ - /* get breakpoint location */ - if (p_td_ta_event_addr (ta, TD_CREATE, ¬ify) != TD_OK) - { - warning ("unable to get location for thread creation breakpoint"); - return; - } - - /* Set up the breakpoint. */ - create_thread_event_breakpoint ((CORE_ADDR) notify.u.bptaddr); - - /* Save it's location. */ - thread_creation_bkpt_address = (CORE_ADDR) notify.u.bptaddr; - - /* thread death */ - /* get breakpoint location */ - if (p_td_ta_event_addr (ta, TD_DEATH, ¬ify) != TD_OK) - { - warning ("unable to get location for thread death breakpoint"); - return; - } - /* Set up the breakpoint. */ - create_thread_event_breakpoint ((CORE_ADDR) notify.u.bptaddr); - - /* Save it's location. */ - thread_death_bkpt_address = (CORE_ADDR) notify.u.bptaddr; -} - -/* This function handles the global parts of disabling thread events. - The thread-specific enabling is handled per-thread elsewhere. */ - -static void -disable_thread_event_reporting (td_thragent_t *ta) -{ - td_thr_events_t events; - - /* set process wide mask saying we aren't interested in any events */ - td_event_emptyset (&events); - p_td_ta_set_event (main_threadagent, &events); - - /* Delete thread event breakpoints, if any. */ - remove_thread_event_breakpoints (); - thread_creation_bkpt_address = 0; - thread_death_bkpt_address = 0; -} - -/* check_for_thread_event - - if it's a thread event we recognize (currently - we only recognize creation and destruction - events), return 1; else return 0. */ - - -static int -check_for_thread_event (struct target_waitstatus *tws, int event_pid) -{ - /* FIXME: to be more efficient, we should keep a static - list of threads, and update it only here (with td_ta_thr_iter). */ - return 0; -} - -static void -thread_db_push_target (void) -{ - /* Called ONLY from thread_db_new_objfile after td_ta_new call succeeds. */ - - /* Push this target vector */ - push_target (&thread_db_ops); - /* Find the underlying process-layer target for calling later. */ - target_beneath = find_target_beneath (&thread_db_ops); - using_thread_db = 1; - /* Turn on thread_db event-reporting API. */ - enable_thread_event_reporting (main_threadagent); -} - -static void -thread_db_unpush_target (void) -{ - /* Must be called whenever we remove ourself from the target stack! */ - - using_thread_db = 0; - target_beneath = NULL; - - /* delete local list of threads */ - empty_threadlist (); - /* Turn off the thread_db API. */ - p_td_ta_delete (main_threadagent); - /* Unpush this target vector */ - unpush_target (&thread_db_ops); - /* Reset linuxthreads module. */ - linuxthreads_discard_global_state (); -} - -/* - * New objfile hook function: - * Called for each new objfile (image, shared lib) in the target process. - * - * The purpose of this function is to detect that the target process - * is linked with the (appropriate) thread library. So every time a - * new target shared library is detected, we will call td_ta_new. - * If it succeeds, we know we have a multi-threaded target process - * that we can debug using the thread_db API. - */ - -/* - * new_objfile function: - * - * connected to target_new_objfile_hook, this function gets called - * every time a new binary image is loaded. - * - * At each call, we attempt to open the thread_db connection to the - * child process. If it succeeds, we know we have a libthread process - * and we can debug it with this target vector. Therefore we push - * ourself onto the target stack. - */ - -static void (*target_new_objfile_chain) (struct objfile *objfile); -static int stop_or_attach_thread_callback (const td_thrhandle_t *th, - void *data); -static int wait_thread_callback (const td_thrhandle_t *th, - void *data); - -static void -thread_db_new_objfile (struct objfile *objfile) -{ - td_err_e ret; - - if (using_thread_db) /* libthread already detected, and */ - goto quit; /* thread target vector activated. */ - - if (objfile == NULL) - goto quit; /* un-interesting object file */ - - /* Initialize our "main prochandle" with the main inferior pid. */ - main_prochandle.pid = PIDGET (inferior_ptid); - - /* Now attempt to open a thread_db connection to the - thread library running in the child process. */ - ret = p_td_ta_new (&main_prochandle, &main_threadagent); - switch (ret) { - default: - warning ("Unexpected error initializing thread_db: %s", - thr_err_string (ret)); - break; - case TD_NOLIBTHREAD: /* expected: no libthread in child process (yet) */ - break; - case TD_OK: /* libthread detected in child: we go live now! */ - thread_db_push_target (); - event_pid = PIDGET (inferior_ptid); /* for resume */ - - /* Now stop everyone else, and attach any new threads you find. */ - p_td_ta_thr_iter (main_threadagent, - stop_or_attach_thread_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, - TD_THR_ANY_USER_FLAGS); - - /* Now go call wait on all the threads you've stopped: - This allows us to absorb the SIGKILL event, and to make sure - that the thread knows that it is stopped (Linux peculiarity). */ - p_td_ta_thr_iter (main_threadagent, - wait_thread_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, - TD_THR_ANY_USER_FLAGS); - - break; - } -quit: - if (target_new_objfile_chain) - target_new_objfile_chain (objfile); -} - - -/* - - LOCAL FUNCTION - - thread_db_alive - test thread for "aliveness" - - SYNOPSIS - - static bool thread_db_alive (int pid); - - DESCRIPTION - - returns true if thread still active in inferior. - - */ - -static int -thread_db_alive (ptid_t ptid) -{ - if (is_thread (ptid)) /* user-space (non-kernel) thread */ - { - td_thrhandle_t th; - td_err_e ret; - int pid = GET_THREAD (ptid); - - if ((ret = p_td_ta_map_id2thr (main_threadagent, pid, &th)) != TD_OK) - return 0; /* thread not found */ - if ((ret = p_td_thr_validate (&th)) != TD_OK) - return 0; /* thread not valid */ - return 1; /* known thread: return true */ - } - else if (target_beneath->to_thread_alive) - return target_beneath->to_thread_alive (ptid); - else - return 0; /* default to "not alive" (shouldn't happen anyway) */ -} - -/* - * get_lwp_from_thread_handle - */ - -static int /* lwpid_t or pid_t */ -get_lwp_from_thread_handle (td_thrhandle_t *th) -{ - td_thrinfo_t ti; - td_err_e ret; - - if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK) - error ("get_lwp_from_thread_handle: thr_get_info failed: %s", - thr_err_string (ret)); - - return ti.ti_lid; -} - -/* - * get_lwp_from_thread_id - */ - -static int /* lwpid_t or pid_t */ -get_lwp_from_thread_id (int tid /* thread_t? */) -{ - td_thrhandle_t th; - td_err_e ret; - - if ((ret = p_td_ta_map_id2thr (main_threadagent, tid, &th)) != TD_OK) - error ("get_lwp_from_thread_id: map_id2thr failed: %s", - thr_err_string (ret)); - - return get_lwp_from_thread_handle (&th); -} - -/* - * pid_to_str has to handle user-space threads. - * If not a user-space thread, then pass the request on to the - * underlying stratum if it can handle it: else call normal_pid_to_str. - */ - -static char * -thread_db_pid_to_str (ptid_t ptid) -{ - static char buf[100]; - td_thrhandle_t th; - td_thrinfo_t ti; - td_err_e ret; - - if (is_thread (ptid)) - { - if ((ret = p_td_ta_map_id2thr (main_threadagent, - GET_THREAD (ptid), - &th)) != TD_OK) - error ("thread_db: map_id2thr failed: %s", thr_err_string (ret)); - - if ((ret = p_td_thr_get_info (&th, &ti)) != TD_OK) - error ("thread_db: thr_get_info failed: %s", thr_err_string (ret)); - - if (ti.ti_state == TD_THR_ACTIVE && - ti.ti_lid != 0) - sprintf (buf, "Thread %ld (LWP %d)", ti.ti_tid, ti.ti_lid); - else - sprintf (buf, "Thread %ld (%s)", ti.ti_tid, - thr_state_string (ti.ti_state)); - } - else if (GET_LWP (ptid)) - sprintf (buf, "LWP %ld", GET_LWP (ptid)); - else return normal_pid_to_str (ptid); - - return buf; -} - -/* - * thread_db target vector functions: - */ - -static void -thread_db_files_info (struct target_ops *tgt_vector) -{ - /* This function will be unnecessary in real life. */ - printf_filtered ("thread_db stratum:\n"); - target_beneath->to_files_info (tgt_vector); -} - -/* - * xfer_memory has to munge the inferior_ptid before passing the call - * down to the target layer. - */ - -static int -thread_db_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int dowrite, - struct mem_attrib *attrib, - struct target_ops *target) -{ - struct cleanup *old_chain; - int ret; - - old_chain = save_inferior_ptid (); - - if (is_thread (inferior_ptid) || - !target_thread_alive (inferior_ptid)) - { - /* FIXME: use the LID/LWP, so that underlying process layer - can read memory from specific threads? */ - inferior_ptid = pid_to_ptid (main_prochandle.pid); - } - - ret = target_beneath->to_xfer_memory (memaddr, myaddr, len, - dowrite, attrib, target); - do_cleanups (old_chain); - return ret; -} - -/* - * fetch_registers has to determine if inferior_ptid is a user-space thread. - * If so, we use the thread_db API to get the registers. - * And if not, we call the underlying process stratum. - */ - -static void -thread_db_fetch_registers (int regno) -{ - td_thrhandle_t thandle; - gdb_prfpregset_t fpregset; - prgregset_t gregset; - thread_t thread; - td_err_e ret; - - if (!is_thread (inferior_ptid)) /* kernel thread */ - { /* pass the request on to the target underneath. */ - target_beneath->to_fetch_registers (regno); - return; - } - - /* convert inferior_ptid into a td_thrhandle_t */ - - if ((thread = GET_THREAD (inferior_ptid)) == 0) - error ("fetch_registers: thread == 0"); - - if ((ret = p_td_ta_map_id2thr (main_threadagent, thread, &thandle)) != TD_OK) - error ("fetch_registers: td_ta_map_id2thr: %s", thr_err_string (ret)); - - /* Get the integer regs: - For the sparc, TD_PARTIALREG means that only i0->i7, l0->l7, - pc and sp are saved (by a thread context switch). */ - if ((ret = p_td_thr_getgregs (&thandle, gregset)) != TD_OK && - ret != TD_PARTIALREG) - error ("fetch_registers: td_thr_getgregs %s", thr_err_string (ret)); - - /* And, now the fp regs */ - if ((ret = p_td_thr_getfpregs (&thandle, &fpregset)) != TD_OK && - ret != TD_NOFPREGS) - error ("fetch_registers: td_thr_getfpregs %s", thr_err_string (ret)); - -/* Note that we must call supply_{g fp}regset *after* calling the td routines - because the td routines call ps_lget* which affect the values stored in the - registers array. */ - - supply_gregset ((gdb_gregset_t *) gregset); - supply_fpregset (&fpregset); - -} - -/* - * store_registers has to determine if inferior_ptid is a user-space thread. - * If so, we use the thread_db API to get the registers. - * And if not, we call the underlying process stratum. - */ - -static void -thread_db_store_registers (int regno) -{ - td_thrhandle_t thandle; - gdb_prfpregset_t fpregset; - prgregset_t gregset; - thread_t thread; - td_err_e ret; - - if (!is_thread (inferior_ptid)) /* Kernel thread: */ - { /* pass the request on to the underlying target vector. */ - target_beneath->to_store_registers (regno); - return; - } - - /* convert inferior_ptid into a td_thrhandle_t */ - - if ((thread = GET_THREAD (inferior_ptid)) == 0) - error ("store_registers: thread == 0"); - - if ((ret = p_td_ta_map_id2thr (main_threadagent, thread, &thandle)) != TD_OK) - error ("store_registers: td_ta_map_id2thr %s", thr_err_string (ret)); - - if (regno != -1) - { /* Not writing all the regs */ - /* save new register value */ - /* MVS: I don't understand this... */ - char old_value[REGISTER_SIZE]; - - memcpy (old_value, ®isters[REGISTER_BYTE (regno)], REGISTER_SIZE); - - if ((ret = p_td_thr_getgregs (&thandle, gregset)) != TD_OK) - error ("store_registers: td_thr_getgregs %s", thr_err_string (ret)); - if ((ret = p_td_thr_getfpregs (&thandle, &fpregset)) != TD_OK) - error ("store_registers: td_thr_getfpregs %s", thr_err_string (ret)); - - /* restore new register value */ - memcpy (®isters[REGISTER_BYTE (regno)], old_value, REGISTER_SIZE); - - } - - fill_gregset ((gdb_gregset_t *) gregset, regno); - fill_fpregset (&fpregset, regno); - - if ((ret = p_td_thr_setgregs (&thandle, gregset)) != TD_OK) - error ("store_registers: td_thr_setgregs %s", thr_err_string (ret)); - if ((ret = p_td_thr_setfpregs (&thandle, &fpregset)) != TD_OK && - ret != TD_NOFPREGS) - error ("store_registers: td_thr_setfpregs %s", thr_err_string (ret)); -} - -static void -handle_new_thread (int tid, /* user thread id */ - int lid, /* kernel thread id */ - int verbose) -{ - ptid_t gdb_ptid = BUILD_THREAD (tid, main_prochandle.pid); - int wait_pid, wait_status; - - if (verbose) - printf_filtered ("[New %s]\n", target_pid_to_str (gdb_ptid)); - add_thread (gdb_ptid); - - if (lid != main_prochandle.pid) - { - attach_thread (lid); - /* According to the Eric Paire model, we now have to send - the restart signal to the new thread -- however, empirically, - I do not find that to be necessary. */ - attach_pid = lid; - } -} - -static void -test_for_new_thread (int tid, int lid, int verbose) -{ - if (!in_thread_list (BUILD_THREAD (tid, main_prochandle.pid))) - handle_new_thread (tid, lid, verbose); -} - -/* - * Callback function that gets called once per USER thread - * (i.e., not kernel) thread by td_ta_thr_iter. - */ - -static int -find_new_threads_callback (const td_thrhandle_t *th, void *ignored) -{ - td_thrinfo_t ti; - td_err_e ret; - - if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK) - { - warning ("find_new_threads_callback: %s", thr_err_string (ret)); - return -1; /* bail out, get_info failed. */ - } - - /* FIXME: - As things now stand, this should never detect a new thread. - But if it does, we could be in trouble because we aren't calling - wait_thread_callback for it. */ - test_for_new_thread (ti.ti_tid, ti.ti_lid, 0); - return 0; -} - -/* - * find_new_threads uses the thread_db iterator function to discover - * user-space threads. Then if the underlying process stratum has a - * find_new_threads method, we call that too. - */ - -static void -thread_db_find_new_threads (void) -{ - if (PIDGET (inferior_ptid) == -1) /* FIXME: still necessary? */ - { - printf_filtered ("No process.\n"); - return; - } - p_td_ta_thr_iter (main_threadagent, - find_new_threads_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, - TD_THR_ANY_USER_FLAGS); - if (target_beneath->to_find_new_threads) - target_beneath->to_find_new_threads (); -} - -/* - * Resume all threads, or resume a single thread. - * If step is true, then single-step the appropriate thread - * (or single-step inferior_ptid, but continue everyone else). - * If signo is true, then send that signal to at least one thread. - */ - -/* - * This function is called once for each thread before resuming. - * It sends continue (no step, and no signal) to each thread except - * the main thread, and - * the event thread (the one that stopped at a breakpoint etc.) - * - * The event thread is handled separately so that it can be sent - * the stepping and signal args with which target_resume was called. - * - * The main thread is resumed last, so that the thread_db proc_service - * callbacks will still work during the iterator function. - */ - -static int -resume_thread_callback (const td_thrhandle_t *th, void *data) -{ - td_thrinfo_t ti; - td_err_e ret; - - if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK) - { - warning ("resume_thread_callback: %s", thr_err_string (ret)); - return -1; /* bail out, get_info failed. */ - } - /* FIXME: - As things now stand, this should never detect a new thread. - But if it does, we could be in trouble because we aren't calling - wait_thread_callback for it. */ - test_for_new_thread (ti.ti_tid, ti.ti_lid, 1); - - if (ti.ti_lid != main_prochandle.pid && - ti.ti_lid != event_pid) - { - /* Unconditionally continue the thread with no signal. - Only the event thread will get a signal of any kind. */ - - target_beneath->to_resume (pid_to_ptid (ti.ti_lid), 0, 0); - } - return 0; -} - -static int -new_resume_thread_callback (threadinfo *thread, void *data) -{ - if (thread->lid != event_pid && - thread->lid != main_prochandle.pid) - { - /* Unconditionally continue the thread with no signal (for now). */ - - target_beneath->to_resume (pid_to_ptid (thread->lid), 0, 0); - } - return 0; -} - -static int last_resume_pid; -static int last_resume_step; -static int last_resume_signo; - -static void -thread_db_resume (ptid_t ptid, int step, enum target_signal signo) -{ - last_resume_pid = PIDGET (ptid); - last_resume_step = step; - last_resume_signo = signo; - - /* resuming a specific pid? */ - if (PIDGET (ptid) != -1) - { - if (is_thread (ptid)) - ptid = pid_to_ptid (get_lwp_from_thread_id (GET_THREAD (ptid))); - else if (GET_LWP (ptid)) - ptid = pid_to_ptid (GET_LWP (ptid)); - } - - /* Apparently the interpretation of 'pid' is dependent on 'step': - If step is true, then a specific pid means 'step only this pid'. - But if step is not true, then pid means 'continue ALL pids, but - give the signal only to this one'. */ - if (PIDGET (ptid) != -1 && step) - { - /* FIXME: is this gonna work in all circumstances? */ - target_beneath->to_resume (ptid, step, signo); - } - else - { - /* 1) Continue all threads except the event thread and the main thread. - 2) resume the event thread with step and signo. - 3) If event thread != main thread, continue the main thread. - - Note: order of 2 and 3 may need to be reversed. */ - - threadlist_iter (new_resume_thread_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_ANY_TYPE); - /* now resume event thread, and if necessary also main thread. */ - if (event_pid) - { - target_beneath->to_resume (pid_to_ptid (event_pid), step, signo); - } - if (event_pid != main_prochandle.pid) - { - target_beneath->to_resume (pid_to_ptid (main_prochandle.pid), 0, 0); - } - } -} - -/* All new threads will be attached. - All previously known threads will be stopped using kill (SIGKILL). */ - -static int -stop_or_attach_thread_callback (const td_thrhandle_t *th, void *data) -{ - td_thrinfo_t ti; - td_err_e ret; - ptid_t gdb_ptid; - int on_off = 1; - - if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK) - { - warning ("stop_or_attach_thread_callback: %s", thr_err_string (ret)); - return -1; /* bail out, get_info failed. */ - } - - /* First add it to our internal list. - We build this list anew at every wait event. */ - insert_thread (ti.ti_tid, ti.ti_lid, ti.ti_state, ti.ti_type); - /* Now: if we've already seen it, stop it, else add it and attach it. */ - gdb_ptid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid); - if (!in_thread_list (gdb_ptid)) /* new thread */ - { - handle_new_thread (ti.ti_tid, ti.ti_lid, 1); - /* Enable thread events */ - if (p_td_thr_event_enable) - if ((ret = p_td_thr_event_enable (th, on_off)) != TD_OK) - warning ("stop_or_attach_thread: %s", thr_err_string (ret)); - } - else if (ti.ti_lid != event_pid && - ti.ti_lid != main_prochandle.pid) - { - ret = (td_err_e) kill (ti.ti_lid, SIGSTOP); - } - - return 0; -} - -/* - * Wait for signal N from pid PID. - * If wait returns any other signals, put them back before returning. - */ - -static void -wait_for_stop (int pid) -{ - int i; - int retpid; - int status; - - /* Array of wait/signal status */ - /* FIXME: wrong data structure, we need a queue. - Realtime signals may be delivered more than once. - And at that, we really can't handle them (see below). */ -#if defined (NSIG) - static int wstatus [NSIG]; -#elif defined (_NSIG) - static int wstatus [_NSIG]; -#else -#error No definition for number of signals! -#endif - - /* clear wait/status list */ - memset (&wstatus, 0, sizeof (wstatus)); - - /* Now look for SIGSTOP event on all threads except event thread. */ - do { - errno = 0; - if (pid == main_prochandle.pid) - retpid = waitpid (pid, &status, 0); - else - retpid = waitpid (pid, &status, __WCLONE); - - if (retpid > 0) - if (WSTOPSIG (status) == SIGSTOP) - { - /* Got the SIGSTOP event we're looking for. - Throw it away, and throw any other events back! */ - for (i = 0; i < sizeof(wstatus) / sizeof (wstatus[0]); i++) - if (wstatus[i]) - if (i != SIGSTOP) - { - kill (retpid, i); - } - break; /* all done */ - } - else - { - int signo; - /* Oops, got an event other than SIGSTOP. - Save it, and throw it back after we find the SIGSTOP event. */ - - /* FIXME (how?) This method is going to fail for realtime - signals, which cannot be put back simply by using kill. */ - - if (WIFEXITED (status)) - error ("Ack! Thread Exited event. What do I do now???"); - else if (WIFSTOPPED (status)) - signo = WSTOPSIG (status); - else - signo = WTERMSIG (status); - - /* If a thread other than the event thread has hit a GDB - breakpoint (as opposed to some random trap signal), then - just arrange for it to hit it again later. Back up the - PC if necessary. Don't forward the SIGTRAP signal to - the thread. We will handle the current event, eventually - we will resume all the threads, and this one will get - it's breakpoint trap again. - - If we do not do this, then we run the risk that the user - will delete or disable the breakpoint, but the thread will - have already tripped on it. */ - - if (retpid != event_pid && - signo == SIGTRAP && - breakpoint_inserted_here_p (read_pc_pid (pid_to_ptid (retpid)) - - DECR_PC_AFTER_BREAK)) - { - /* Set the pc to before the trap and DO NOT re-send the signal */ - if (DECR_PC_AFTER_BREAK) - write_pc_pid (read_pc_pid (pid_to_ptid (retpid)) - - DECR_PC_AFTER_BREAK, - pid_to_ptid (retpid)); - } - - /* Since SIGINT gets forwarded to the entire process group - (in the case where ^C is typed at the tty / console), - just ignore all SIGINTs from other than the event thread. */ - else if (retpid != event_pid && signo == SIGINT) - { /* do nothing. Signal will disappear into oblivion! */ - ; - } - - else /* This is some random signal other than a breakpoint. */ - { - wstatus [signo] = 1; - } - child_resume (pid_to_ptid (retpid), 0, TARGET_SIGNAL_0); - continue; - } - - } while (errno == 0 || errno == EINTR); -} - -/* - * wait_thread_callback - * - * Calls waitpid for each thread, repeatedly if necessary, until - * SIGSTOP is returned. Afterward, if any other signals were returned - * by waitpid, return them to the thread's pending queue by calling kill. - */ - -static int -wait_thread_callback (const td_thrhandle_t *th, void *data) -{ - td_thrinfo_t ti; - td_err_e ret; - - if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK) - { - warning ("wait_thread_callback: %s", thr_err_string (ret)); - return -1; /* bail out, get_info failed. */ - } - - /* This callback to act on all threads except the event thread: */ - if (ti.ti_lid == event_pid || /* no need to wait (no sigstop) */ - ti.ti_lid == main_prochandle.pid) /* no need to wait (already waited) */ - return 0; /* don't wait on the event thread. */ - - wait_for_stop (ti.ti_lid); - return 0; /* finished: next thread. */ -} - -static int -new_wait_thread_callback (threadinfo *thread, void *data) -{ - /* don't wait on the event thread -- it's already stopped and waited. - Ditto the main thread. */ - if (thread->lid != event_pid && - thread->lid != main_prochandle.pid) - { - wait_for_stop (thread->lid); - } - return 0; -} - -/* - * Wait for any thread to stop, by calling the underlying wait method. - * The PID returned by the underlying target may be a kernel thread, - * in which case we will want to convert it to the corresponding - * user-space thread. - */ - -static ptid_t -thread_db_wait (ptid_t ptid, struct target_waitstatus *ourstatus) -{ - td_thrhandle_t thandle; - td_thrinfo_t ti; - td_err_e ret; - lwpid_t lwp; - int retpid; - ptid_t retptid; - int status; - int save_errno; - - /* OK, we're about to wait for an event from the running inferior. - Make sure we're ignoring the right signals. */ - - check_all_signal_numbers (); /* see if magic signals changed. */ - - event_pid = 0; - attach_pid = 0; - - /* FIXME: should I do the wait right here inline? */ -#if 0 - if (PIDGET (ptid) == -1) - lwp = -1; - else - lwp = get_lwp_from_thread_id (GET_THREAD (ptid)); -#endif - - - save_errno = linux_child_wait (-1, &retpid, &status); - store_waitstatus (ourstatus, status); - - /* Thread ID is irrelevant if the target process exited. - FIXME: do I have any killing to do? - Can I get this event mistakenly from a thread? */ - if (ourstatus->kind == TARGET_WAITKIND_EXITED) - return pid_to_ptid (retpid); - - /* OK, we got an event of interest. - Go stop all threads and look for new ones. - FIXME: maybe don't do this for the restart signal? Optimization... */ - event_pid = retpid; - - /* If the last call to resume was for a specific thread, then we don't - need to stop everyone else: they should already be stopped. */ - if (last_resume_step == 0 || last_resume_pid == -1) - { - /* Main thread must be stopped before calling the iterator. */ - if (retpid != main_prochandle.pid) - { - kill (main_prochandle.pid, SIGSTOP); - wait_for_stop (main_prochandle.pid); - } - - empty_threadlist (); - /* Now stop everyone else, and attach any new threads you find. */ - p_td_ta_thr_iter (main_threadagent, - stop_or_attach_thread_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, - TD_THR_ANY_USER_FLAGS); - - /* Now go call wait on all the threads we've stopped: - This allows us to absorb the SIGKILL event, and to make sure - that the thread knows that it is stopped (Linux peculiarity). */ - - threadlist_iter (new_wait_thread_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_ANY_TYPE); - } - - /* Convert the kernel thread id to the corresponding thread id. */ - - /* If the process layer does not furnish an lwp, - then perhaps the returned pid IS the lwp... */ -#if 0 /* Always true (if it'd compile...) */ - if ((lwp = GET_LWP (pid_to_ptid (retpid))) == 0) -#endif - lwp = retpid; - - if ((ret = p_td_ta_map_lwp2thr (main_threadagent, lwp, &thandle)) != TD_OK) - return pid_to_ptid (retpid); /* LWP is not mapped onto a user-space thread. */ - - if ((ret = p_td_thr_validate (&thandle)) != TD_OK) - return pid_to_ptid (retpid); /* LWP is not mapped onto a valid thread. */ - - if ((ret = p_td_thr_get_info (&thandle, &ti)) != TD_OK) - { - warning ("thread_db: thr_get_info failed ('%s')", thr_err_string (ret)); - return pid_to_ptid (retpid); - } - - retptid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid); - /* If this is a new user thread, notify GDB about it. */ - if (!in_thread_list (retptid)) - { - printf_filtered ("[New %s]\n", target_pid_to_str (retptid)); - add_thread (retptid); - } - -#if 0 - /* Now detect if this is a thread creation/deletion event: */ - check_for_thread_event (ourstatus, retpid); -#endif - return retptid; -} - -/* - * kill has to call the underlying kill. - * FIXME: I'm not sure if it's necessary to check inferior_ptid any more, - * but we might need to fix inferior_ptid up if it's a user thread. - */ - -static int -kill_thread_callback (const td_thrhandle_t *th, void *data) -{ - td_thrinfo_t ti; - td_err_e ret; - - /* Fixme: - For Linux, threads may need to be waited. */ - if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK) - { - warning ("kill_thread_callback: %s", thr_err_string (ret)); - return -1; /* bail out, get_info failed. */ - } - - if (ti.ti_lid != main_prochandle.pid) - { - kill (ti.ti_lid, SIGKILL); - } - return 0; -} - - -static void thread_db_kill (void) -{ - int rpid; - int status; - - /* Fixme: - For Linux, threads may need to be waited. */ - if (! ptid_equal (inferior_ptid, null_ptid)) - { - /* Go kill the children first. Save the main thread for last. */ - p_td_ta_thr_iter (main_threadagent, - kill_thread_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, - TD_THR_ANY_USER_FLAGS); - - /* Turn off thread_db event-reporting API *before* killing the - main thread, since this operation requires child memory access. - Can't move this into thread_db_unpush target because then - detach would not work. */ - disable_thread_event_reporting (main_threadagent); - - inferior_ptid = pid_to_ptid (main_prochandle.pid); - - /* - * Since both procfs_kill and ptrace_kill call target_mourn, - * it should be sufficient for me to call one of them. - * That will result in my mourn being called, which will both - * unpush me and call the underlying mourn. - */ - target_beneath->to_kill (); - } - - /* Wait for all threads. */ - /* FIXME: need a universal wait_for_signal func? */ - do - { - rpid = waitpid (-1, &status, __WCLONE | WNOHANG); - } - while (rpid > 0 || errno == EINTR); - - do - { - rpid = waitpid (-1, &status, WNOHANG); - } - while (rpid > 0 || errno == EINTR); -} - -/* - * Mourn has to remove us from the target stack, - * and then call the underlying mourn. - */ - -static void thread_db_mourn_inferior (void) -{ - thread_db_unpush_target (); - target_mourn_inferior (); /* call the underlying mourn */ -} - -/* - * Detach has to remove us from the target stack, - * and then call the underlying detach. - * - * But first, it has to detach all the cloned threads! - */ - -static int -detach_thread_callback (const td_thrhandle_t *th, void *data) -{ - /* Called once per thread. */ - td_thrinfo_t ti; - td_err_e ret; - - if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK) - { - warning ("detach_thread_callback: %s", thr_err_string (ret)); - return -1; /* bail out, get_info failed. */ - } - - if (!in_thread_list (BUILD_THREAD (ti.ti_tid, main_prochandle.pid))) - return 0; /* apparently we don't know this one. */ - - /* Save main thread for last, or the iterator will fail! */ - if (ti.ti_lid != main_prochandle.pid) - { - struct cleanup *old_chain; - int off = 0; - - /* Time to detach this thread. - First disable thread_db event reporting for the thread. */ - if (p_td_thr_event_enable && - (ret = p_td_thr_event_enable (th, off)) != TD_OK) - { - warning ("detach_thread_callback: %s\n", thr_err_string (ret)); - return 0; - } - - /* Now cancel any pending SIGTRAPS. FIXME! */ - - /* Call underlying detach method. FIXME just detach it. */ - old_chain = save_inferior_ptid (); - inferior_ptid = pid_to_ptid (ti.ti_lid); - detach (TARGET_SIGNAL_0); - do_cleanups (old_chain); - } - return 0; -} - -static void -thread_db_detach (char *args, int from_tty) -{ - td_err_e ret; - - if ((ret = p_td_ta_thr_iter (main_threadagent, - detach_thread_callback, - (void *) 0, - TD_THR_ANY_STATE, - TD_THR_LOWEST_PRIORITY, - TD_SIGNO_MASK, - TD_THR_ANY_USER_FLAGS)) - != TD_OK) - warning ("detach (thr_iter): %s", thr_err_string (ret)); - - /* Turn off thread_db event-reporting API - (before detaching the main thread) */ - disable_thread_event_reporting (main_threadagent); - - thread_db_unpush_target (); - - /* above call nullifies target_beneath, so don't use that! */ - inferior_ptid = pid_to_ptid (PIDGET (inferior_ptid)); - target_detach (args, from_tty); -} - - -/* - * We never want to actually create the inferior! - * - * If this is ever called, it means we were on the target stack - * when the user said "run". But we don't want to be on the new - * inferior's target stack until the thread_db / libthread - * connection is ready to be made. - * - * So, what shall we do? - * Unpush ourselves from the stack, and then invoke - * find_default_create_inferior, which will invoke the - * appropriate process_stratum target to do the create. - */ - -static void -thread_db_create_inferior (char *exec_file, char *allargs, char **env) -{ - thread_db_unpush_target (); - find_default_create_inferior (exec_file, allargs, env); -} - -/* - * Thread_db target vector initializer. - */ - -void -init_thread_db_ops (void) -{ - thread_db_ops.to_shortname = "multi-thread"; - thread_db_ops.to_longname = "multi-threaded child process."; - thread_db_ops.to_doc = "Threads and pthreads support."; - thread_db_ops.to_files_info = thread_db_files_info; - thread_db_ops.to_create_inferior = thread_db_create_inferior; - thread_db_ops.to_detach = thread_db_detach; - thread_db_ops.to_wait = thread_db_wait; - thread_db_ops.to_resume = thread_db_resume; - thread_db_ops.to_mourn_inferior = thread_db_mourn_inferior; - thread_db_ops.to_kill = thread_db_kill; - thread_db_ops.to_xfer_memory = thread_db_xfer_memory; - thread_db_ops.to_fetch_registers = thread_db_fetch_registers; - thread_db_ops.to_store_registers = thread_db_store_registers; - thread_db_ops.to_thread_alive = thread_db_alive; - thread_db_ops.to_find_new_threads = thread_db_find_new_threads; - thread_db_ops.to_pid_to_str = thread_db_pid_to_str; - thread_db_ops.to_stratum = thread_stratum; - thread_db_ops.to_has_thread_control = tc_schedlock; - thread_db_ops.to_magic = OPS_MAGIC; -} -#endif /* HAVE_STDINT_H */ - -/* - * Module constructor / initializer function. - * If connection to thread_db dynamic library is successful, - * then initialize this module's target vectors and the - * new_objfile hook. - */ - - -void -_initialize_thread_db (void) -{ -#ifdef HAVE_STDINT_H /* stub out entire module, leave initializer empty */ - if (init_thread_db_library ()) - { - init_thread_db_ops (); - add_target (&thread_db_ops); - /* - * Hook up to the new_objfile event. - * If someone is already there, arrange for him to be called - * after we are. - */ - target_new_objfile_chain = target_new_objfile_hook; - target_new_objfile_hook = thread_db_new_objfile; - } -#endif /* HAVE_STDINT_H */ -} - |