Browse Source
* config/alpha/alpha-osf3.mh (NATDEPFILES): Add dec-thread.o.
(NAT_CLIBS): Define.
cgen-1_1-branch
3 changed files with 691 additions and 1 deletions
@ -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); |
|||
} |
|||
Loading…
Reference in new issue