diff options
author | Joel Brobecker <brobecker@gnat.com> | 2008-10-20 15:48:17 +0000 |
---|---|---|
committer | Joel Brobecker <brobecker@gnat.com> | 2008-10-20 15:48:17 +0000 |
commit | 7a052092c3500a99f4a54e3b8efaa566211fe9bc (patch) | |
tree | 666263422d8860389691bee9b33a8717b1ac320d /gdb/dec-thread.c | |
parent | cb8f3167e32dbe8b0f4dc4c2dc28823b03d8c8ac (diff) | |
download | gdb-7a052092c3500a99f4a54e3b8efaa566211fe9bc.zip gdb-7a052092c3500a99f4a54e3b8efaa566211fe9bc.tar.gz gdb-7a052092c3500a99f4a54e3b8efaa566211fe9bc.tar.bz2 |
* dec-thread.c: New file.
* config/alpha/alpha-osf3.mh (NATDEPFILES): Add dec-thread.o.
(NAT_CLIBS): Define.
Diffstat (limited to 'gdb/dec-thread.c')
-rw-r--r-- | gdb/dec-thread.c | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/gdb/dec-thread.c b/gdb/dec-thread.c new file mode 100644 index 0000000..7f26166 --- /dev/null +++ b/gdb/dec-thread.c @@ -0,0 +1,683 @@ +/* Copyright (C) 2008 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 3 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, see <http://www.gnu.org/licenses/>. */ + +#include "defs.h" +#include "command.h" +#include "gdbcmd.h" +#include "target.h" +#include "observer.h" +#include <sys/procfs.h> +#include "gregset.h" +#include "regcache.h" +#include "inferior.h" +#include "gdbthread.h" + +#include <pthread_debug.h> + +/* Print debugging traces if set to non-zero. */ +static int debug_dec_thread = 0; + +/* Non-zero if the dec-thread layer is active. */ +static int dec_thread_active = 0; + +/* The pthread_debug context. */ +pthreadDebugContext_t debug_context; + +/* The dec-thread target_ops structure. */ +static struct target_ops dec_thread_ops; + +/* A copy of the target_ops over which our dec_thread_ops is pushed. */ +static struct target_ops base_target; + +/* Print a debug trace if DEBUG_DEC_THREAD is set (its value is adjusted + by the user using "set debug dec-thread ..."). */ + +static void +debug (char *format, ...) +{ + if (debug_dec_thread) + { + va_list args; + + va_start (args, format); + printf_unfiltered ("DEC Threads: "); + vprintf_unfiltered (format, args); + printf_unfiltered ("\n"); + va_end (args); + } +} + +/* pthread debug callbacks. */ + +static int +suspend_clbk (void *caller_context) +{ + return ESUCCESS; +} + +static int +resume_clbk (void *caller_context) +{ + return ESUCCESS; +} + +static int +hold_clbk (void *caller_context, pthreadDebugKId_t kernel_tid) +{ + return ESUCCESS; +} + +static int +unhold_clbk (void *caller_context, pthreadDebugKId_t kernel_tid) +{ + return ESUCCESS; +} + +static int +read_clbk (void *caller_context, void *address, void *buffer, + unsigned long size) +{ + int status = target_read_memory ((CORE_ADDR) address, buffer, size); + + if (status != 0) + return EINVAL; + + return ESUCCESS; +} + +static int +write_clbk (void *caller_context, void *address, void *buffer, + unsigned long size) +{ + int status = target_write_memory ((CORE_ADDR) address, buffer, size); + + if (status != 0) + return EINVAL; + + return ESUCCESS; +} + +/* Get integer regs */ + +static int +get_reg_clbk(void *caller_context, pthreadDebugGetRegRtn_t regs, + pthreadDebugKId_t kernel_tid) +{ + debug ("get_reg_clbk"); + + /* Not sure that we actually need to do anything in this callback. */ + return ESUCCESS; +} + +/* Set integer regs */ + +static int +set_reg_clbk(void *caller_context, const pthreadDebugRegs_t *regs, + pthreadDebugKId_t kernel_tid) +{ + debug ("set_reg_clbk"); + + /* Not sure that we actually need to do anything in this callback. */ + return ESUCCESS; +} + +static int +output_clbk (void *caller_context, char *line) +{ + printf_filtered ("%s\n", line); + return ESUCCESS; +} + +static int +error_clbk (void *caller_context, char *line) +{ + fprintf_filtered (gdb_stderr, "%s\n", line); + return ESUCCESS; +} + +/* Get floating-point regs. */ + +static int +get_fpreg_clbk (void *caller_context, pthreadDebugFregs_p fregs, + pthreadDebugKId_t kernel_tid) +{ + debug ("get_fpreg_clbk"); + + /* Not sure that we actually need to do anything in this callback. */ + return ESUCCESS; +} + +/* Set floating-point regs. */ + +static int +set_fpreg_clbk (void *caller_context, const pthreadDebugFregs_t *fregs, + pthreadDebugKId_t kernel_tid) +{ + debug ("set_fpreg_clbk"); + + /* Not sure that we actually need to do anything in this callback. */ + return ESUCCESS; +} + +static void * +malloc_clbk (void *caller_context, size_t size) +{ + return xmalloc (size); +} + +static void +free_clbk (void *caller_context, void *address) +{ + xfree (address); +} + +static int +kthdinfo_clbk (pthreadDebugClient_t caller_context, + pthreadDebugKId_t kernel_tid, + pthreadDebugKThreadInfo_p thread_info) +{ + return ENOTSUP; +} + +static int +speckthd_clbk (pthreadDebugClient_t caller_context, + pthreadDebugSpecialType_t type, + pthreadDebugKId_t *kernel_tid) +{ + return ENOTSUP; +} + +static pthreadDebugCallbacks_t debug_callbacks = +{ + PTHREAD_DEBUG_VERSION, + (pthreadDebugGetMemRtn_t) read_clbk, + (pthreadDebugSetMemRtn_t) write_clbk, + suspend_clbk, + resume_clbk, + kthdinfo_clbk, + hold_clbk, + unhold_clbk, + (pthreadDebugGetFregRtn_t) get_fpreg_clbk, + (pthreadDebugSetFregRtn_t) set_fpreg_clbk, + (pthreadDebugGetRegRtn_t) get_reg_clbk, + (pthreadDebugSetRegRtn_t) set_reg_clbk, + (pthreadDebugOutputRtn_t) output_clbk, + (pthreadDebugOutputRtn_t) error_clbk, + malloc_clbk, + free_clbk, + speckthd_clbk +}; + +/* Activate thread support if appropriate. Do nothing if thread + support is already active. */ + +static void +enable_dec_thread (void) +{ + struct minimal_symbol *msym; + void* caller_context; + int status; + + /* If already active, nothing more to do. */ + if (dec_thread_active) + return; + + msym = lookup_minimal_symbol ("__pthread_dbg_symtable", NULL, NULL); + if (msym == NULL) + { + debug ("enable_dec_thread: No __pthread_dbg_symtable"); + return; + } + + status = pthreadDebugContextInit (&caller_context, &debug_callbacks, + (void *) SYMBOL_VALUE_ADDRESS (msym), + &debug_context); + if (status != ESUCCESS) + { + debug ("enable_dec_thread: pthreadDebugContextInit -> %d", + status); + return; + } + + base_target = current_target; + push_target (&dec_thread_ops); + dec_thread_active = 1; + + debug ("enable_dec_thread: Thread support enabled."); +} + +/* Deactivate thread support. Do nothing is thread support is + already inactive. */ + +static void +disable_dec_thread (void) +{ + if (!dec_thread_active) + return; + + pthreadDebugContextDestroy (debug_context); + unpush_target (&dec_thread_ops); + dec_thread_active = 0; +} + +/* A structure that contains a thread ID and is associated + pthreadDebugThreadInfo_t data. */ + +struct dec_thread_info +{ + pthreadDebugId_t thread; + pthreadDebugThreadInfo_t info; +}; +typedef struct dec_thread_info dec_thread_info_s; + +/* The list of user threads. */ + +DEF_VEC_O (dec_thread_info_s); +VEC(dec_thread_info_s) *dec_thread_list; + +/* Release the memory used by the given VECP thread list pointer. + Then set *VECP to NULL. */ + +static void +free_dec_thread_info_vec (VEC(dec_thread_info_s) **vecp) +{ + int i; + struct dec_thread_info *item; + VEC(dec_thread_info_s) *vec = *vecp; + + for (i = 0; VEC_iterate (dec_thread_info_s, vec, i, item); i++) + xfree (item); + VEC_free (dec_thread_info_s, vec); + *vecp = NULL; +} + +/* Return a thread's ptid given its associated INFO. */ + +static ptid_t +ptid_build_from_info (struct dec_thread_info info) +{ + int pid = ptid_get_pid (inferior_ptid); + + return ptid_build (pid, 0, (long) info.thread); +} + +/* Recompute the list of user threads and store the result in + DEC_THREAD_LIST. */ + +static void +update_dec_thread_list (void) +{ + pthreadDebugId_t thread; + pthreadDebugThreadInfo_t info; + int res; + + free_dec_thread_info_vec (&dec_thread_list); + res = pthreadDebugThdSeqInit (debug_context, &thread); + while (res == ESUCCESS) + { + + res = pthreadDebugThdGetInfo (debug_context, thread, &info); + if (res != ESUCCESS) + warning (_("unable to get thread info, ignoring thread %ld"), + thread); + else if (info.kind == PTHREAD_DEBUG_THD_KIND_INITIAL + || info.kind == PTHREAD_DEBUG_THD_KIND_NORMAL) + { + struct dec_thread_info *item = + xmalloc (sizeof (struct dec_thread_info)); + + item->thread = thread; + item->info = info; + VEC_safe_push (dec_thread_info_s, dec_thread_list, item); + } + res = pthreadDebugThdSeqNext (debug_context, &thread); + } + pthreadDebugThdSeqDestroy (debug_context); +} + +/* A callback to count the number of threads known to GDB. */ + +static int +dec_thread_count_gdb_threads (struct thread_info *ignored, void *context) +{ + int *count = (int *) context; + + *count++; + return 0; +} + +/* A callback that saves the given thread INFO at the end of an + array. The end of the array is given in the CONTEXT and is + incremented once the info has been added. */ + +static int +dec_thread_add_gdb_thread (struct thread_info *info, void *context) +{ + struct thread_info ***listp = (struct thread_info ***) context; + + **listp = info; + *listp++; + return 0; +} + +/* Resynchronize the list of threads known by GDB with the actual + list of threads reported by libpthread_debug. */ + +static void +resync_thread_list (void) +{ + int i; + struct dec_thread_info *info; + int num_gdb_threads = 0; + struct thread_info **gdb_thread_list; + struct thread_info **next_thread_info; + + update_dec_thread_list (); + + /* Add new threads. */ + + for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info); + i++) + { + ptid_t ptid = ptid_build_from_info (*info); + + if (!in_thread_list (ptid)) + add_thread (ptid); + } + + /* Remove threads that no longer exist. To help with the search, + we build an array of GDB threads, and then iterate over this + array. */ + + iterate_over_threads (dec_thread_count_gdb_threads, + (void *) &num_gdb_threads); + gdb_thread_list = alloca (num_gdb_threads * sizeof (struct thread_info *)); + next_thread_info = gdb_thread_list; + iterate_over_threads (dec_thread_add_gdb_thread, (void *) &next_thread_info); + for (i = 0; i < num_gdb_threads; i++) + { + int j; + + for (j = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, j, info); + j++) + if (ptid_equal (gdb_thread_list[i]->ptid, + ptid_build_from_info (*info))) + break; + delete_thread (gdb_thread_list[i]->ptid); + } +} + +/* The "to_detach" method of the dec_thread_ops. */ + +static void +dec_thread_detach (char *args, int from_tty) +{ + debug ("dec_thread_detach"); + + disable_dec_thread (); + base_target.to_detach (args, from_tty); +} + +/* Return the ptid of the thread that is currently active. */ + +static ptid_t +get_active_ptid (void) +{ + int i; + struct dec_thread_info *info; + + for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info); + i++) + if (info->info.state == PTHREAD_DEBUG_STATE_RUNNING) + return ptid_build_from_info (*info); + + /* No active thread found. This can happen when the program + has just exited. */ + return null_ptid; +} + +/* The "to_wait" method of the dec_thread_ops. */ + +static ptid_t +dec_thread_wait (ptid_t ptid, struct target_waitstatus *status) +{ + ptid_t active_ptid; + + debug ("dec_thread_wait"); + + ptid = base_target.to_wait (ptid, status); + + /* The ptid returned by the base_target is the ptid of the process. + We need to find which thread is currently active and return its + ptid. */ + resync_thread_list (); + active_ptid = get_active_ptid (); + if (ptid_equal (active_ptid, null_ptid)) + return ptid; + return active_ptid; +} + +/* Fetch the general purpose and floating point registers for the given + thread TID, and store the result in GREGSET and FPREGSET. Return + zero if successful. */ + +static int +dec_thread_get_regsets (pthreadDebugId_t tid, gdb_gregset_t *gregset, + gdb_fpregset_t *fpregset) +{ + int res; + pthreadDebugRegs_t regs; + pthreadDebugFregs_t fregs; + + res = pthreadDebugThdGetReg (debug_context, tid, ®s); + if (res != ESUCCESS) + { + debug ("dec_thread_fetch_registers: pthreadDebugThdGetReg -> %d", res); + return -1; + } + memcpy (gregset->regs, ®s, sizeof (regs)); + + res = pthreadDebugThdGetFreg (debug_context, tid, &fregs); + if (res != ESUCCESS) + { + debug ("dec_thread_fetch_registers: pthreadDebugThdGetFreg -> %d", res); + return -1; + } + memcpy (fpregset->regs, &fregs, sizeof (fregs)); + + return 0; +} + +/* The "to_fetch_registers" method of the dec_thread_ops. + + Because the dec-thread debug API doesn't allow us to fetch + only one register, we simply ignore regno and fetch+supply all + registers. */ + +static void +dec_thread_fetch_registers (struct regcache *regcache, int regno) +{ + pthreadDebugId_t tid = ptid_get_tid (inferior_ptid); + gregset_t gregset; + fpregset_t fpregset; + int res; + + debug ("dec_thread_fetch_registers (tid=%ld, regno=%d)", tid, regno); + + + if (tid == 0 || ptid_equal (inferior_ptid, get_active_ptid ())) + { + base_target.to_fetch_registers (regcache, regno); + return; + } + + res = dec_thread_get_regsets (tid, &gregset, &fpregset); + if (res != 0) + return; + + supply_gregset (regcache, &gregset); + supply_fpregset (regcache, &fpregset); +} + +/* Store the registers given in GREGSET and FPREGSET into the associated + general purpose and floating point registers of thread TID. Return + zero if successful. */ + +static int +dec_thread_set_regsets (pthreadDebugId_t tid, gdb_gregset_t gregset, + gdb_fpregset_t fpregset) +{ + int res; + pthreadDebugRegs_t regs; + pthreadDebugFregs_t fregs; + + memcpy (®s, gregset.regs, sizeof (regs)); + res = pthreadDebugThdSetReg (debug_context, tid, ®s); + if (res != ESUCCESS) + { + debug ("dec_thread_fetch_registers: pthreadDebugThdSetReg -> %d", res); + return -1; + } + + memcpy (&fregs, fpregset.regs, sizeof (fregs)); + res = pthreadDebugThdSetFreg (debug_context, tid, &fregs); + if (res != ESUCCESS) + { + debug ("dec_thread_fetch_registers: pthreadDebugThdSetFreg -> %d", res); + return -1; + } + + return 0; +} + +/* The "to_store_registers" method of the dec_thread_ops. + + Because the dec-thread debug API doesn't allow us to store + just one register, we store all the registers. */ + +static void +dec_thread_store_registers (struct regcache *regcache, int regno) +{ + pthreadDebugId_t tid = ptid_get_tid (inferior_ptid); + gregset_t gregset; + fpregset_t fpregset; + int res; + + debug ("dec_thread_store_registers (tid=%ld, regno=%d)", tid, regno); + + if (tid == 0 || ptid_equal (inferior_ptid, get_active_ptid ())) + { + base_target.to_store_registers (regcache, regno); + return; + } + + /* FIXME: brobecker/2008-05-28: I wonder if we could simply check + in which register set the register is and then only store the + registers for that register set, instead of storing both register + sets. */ + fill_gregset (regcache, &gregset, -1); + fill_fpregset (regcache, &fpregset, -1); + + res = dec_thread_set_regsets (tid, gregset, fpregset); + if (res != 0) + warning (_("failed to store registers.")); +} + +/* The "to_mourn_inferior" method of the dec_thread_ops. */ + +static void +dec_thread_mourn_inferior (void) +{ + debug ("dec_thread_mourn_inferior"); + + disable_dec_thread (); + base_target.to_mourn_inferior (); +} + +/* The "to_thread_alive" method of the dec_thread_ops. */ +static int +dec_thread_thread_alive (ptid_t ptid) +{ + debug ("dec_thread_thread_alive (tid=%ld)", ptid_get_tid (ptid)); + + /* The thread list maintained by GDB is up to date, since we update + it everytime we stop. So check this list. */ + return in_thread_list (ptid); +} + +/* The "to_pid_to_str" method of the dec_thread_ops. */ + +static char * +dec_thread_pid_to_str (ptid_t ptid) +{ + static char *ret = NULL; + + if (ptid_get_tid (ptid) == 0) + return base_target.to_pid_to_str (ptid); + + /* Free previous return value; a new one will be allocated by + xstrprintf(). */ + xfree (ret); + + ret = xstrprintf (_("Thread %ld"), ptid_get_tid (ptid)); + return ret; +} + +/* A "new-objfile" observer. Used to activate/deactivate dec-thread + support. */ + +static void +dec_thread_new_objfile_observer (struct objfile *objfile) +{ + if (objfile != NULL) + enable_dec_thread (); + else + disable_dec_thread (); +} + +static void +init_dec_thread_ops (void) +{ + dec_thread_ops.to_shortname = "dec-threads"; + dec_thread_ops.to_longname = _("DEC threads support"); + dec_thread_ops.to_doc = _("DEC threads support"); + dec_thread_ops.to_detach = dec_thread_detach; + dec_thread_ops.to_wait = dec_thread_wait; + dec_thread_ops.to_fetch_registers = dec_thread_fetch_registers; + dec_thread_ops.to_store_registers = dec_thread_store_registers; + dec_thread_ops.to_mourn_inferior = dec_thread_mourn_inferior; + dec_thread_ops.to_thread_alive = dec_thread_thread_alive; + dec_thread_ops.to_pid_to_str = dec_thread_pid_to_str; + dec_thread_ops.to_stratum = thread_stratum; + dec_thread_ops.to_magic = OPS_MAGIC; +} + +void +_initialize_dec_thread (void) +{ + init_dec_thread_ops (); + add_target (&dec_thread_ops); + + observer_attach_new_objfile (dec_thread_new_objfile_observer); + + add_setshow_boolean_cmd ("dec-thread", class_maintenance, &debug_dec_thread, + _("Set debugging of DEC threads module."), + _("Show debugging of DEC threads module."), + _("Enables debugging output (used to debug GDB)."), + NULL, NULL, + &setdebuglist, &showdebuglist); +} |