3b7344d5ab
This patch changes a few minimal symbol lookup functions to return a bound_minimal_symbol rather than a pointer to the minsym. This change helps prepare gdb for computing a minimal symbol's address at the point of use. Note that this changes even those functions that ostensibly search a single objfile. That was necessary because, in fact, those functions can search an objfile and its separate debug objfiles; and it is important for the caller to know in which objfile the minimal symbol was actually found. The bulk of this patch is mechanical. 2014-02-26 Tom Tromey <tromey@redhat.com> * ada-lang.c (ada_update_initial_language): Update. (ada_main_name, ada_has_this_exception_support): Update. * ada-tasks.c (ada_tasks_inferior_data_sniffer): Update. * aix-thread.c (pdc_symbol_addrs, pd_enable): Update. * arm-tdep.c (arm_skip_stub): Update. * auxv.c (ld_so_xfer_auxv): Update. * avr-tdep.c (avr_scan_prologue): Update. * ax-gdb.c (gen_var_ref): Update. * breakpoint.c (struct breakpoint_objfile_data) <overlay_msym, longjmp_msym, terminate_msym, exception_msym>: Change type to bound_minimal_symbol. (create_overlay_event_breakpoint) (create_longjmp_master_breakpoint) (create_std_terminate_master_breakpoint) (create_exception_master_breakpoint): Update. * bsd-uthread.c (bsd_uthread_lookup_address): Update. * c-exp.y (classify_name): Update. * coffread.c (coff_symfile_read): Update. * common/agent.c (agent_look_up_symbols): Update. * d-lang.c (d_main_name): Update. * dbxread.c (find_stab_function_addr, end_psymtab): Update. * dec-thread.c (enable_dec_thread): Update. * dwarf2loc.c (call_site_to_target_addr): Update. * elfread.c (elf_gnu_ifunc_resolve_by_got): Update. * eval.c (evaluate_subexp_standard): Update. * findvar.c (struct minsym_lookup_data) <result>: Change type to bound_minimal_symbol. <objfile>: Remove. (minsym_lookup_iterator_cb, default_read_var_value): Update. * frame.c (inside_main_func): Update. * frv-tdep.c (frv_frame_this_id): Update. * gcore.c (call_target_sbrk): Update. * glibc-tdep.c (glibc_skip_solib_resolver): Update. * gnu-v3-abi.c (gnuv3_get_typeid, gnuv3_skip_trampoline): Update. * go-lang.c (go_main_name): Update. * hppa-hpux-tdep.c (hppa_hpux_skip_trampoline_code) (hppa_hpux_find_import_stub_for_addr): Update. * hppa-tdep.c (hppa_extract_17, hppa_lookup_stub_minimal_symbol): Update. Change return type. * hppa-tdep.h (hppa_lookup_stub_minimal_symbol): Change return type. * jit.c (jit_breakpoint_re_set_internal): Update. * linux-fork.c (inferior_call_waitpid, checkpoint_command): Update. * linux-nat.c (get_signo): Update. * linux-thread-db.c (inferior_has_bug): Update * m32c-tdep.c (m32c_return_value) (m32c_m16c_address_to_pointer): Update. * m32r-tdep.c (m32r_frame_this_id): Update. * m68hc11-tdep.c (m68hc11_get_register_info): Update. * machoread.c (macho_resolve_oso_sym_with_minsym): Update. * minsyms.c (lookup_minimal_symbol_internal): Rename to lookup_minimal_symbol. Change return type. (lookup_minimal_symbol): Remove. (lookup_bound_minimal_symbol): Update. (lookup_minimal_symbol_text): Change return type. (lookup_minimal_symbol_solib_trampoline): Change return type. * minsyms.h (lookup_minimal_symbol, lookup_minimal_symbol_text) (lookup_minimal_symbol_solib_trampoline): Change return type. * mips-linux-tdep.c (mips_linux_skip_resolver): Update. * objc-lang.c (lookup_objc_class, lookup_child_selector) (value_nsstring, find_imps): Update. * obsd-tdep.c (obsd_skip_solib_resolver): Update. * p-lang.c (pascal_main_name): Update. * ppc-linux-tdep.c (ppc_linux_spe_context_lookup): Update. * ppc-sysv-tdep.c (convert_code_addr_to_desc_addr): Update. * proc-service.c (ps_pglobal_lookup): Update. * ravenscar-thread.c (get_running_thread_msymbol): Change return type. (has_ravenscar_runtime, get_running_thread_id): Update. * remote.c (remote_check_symbols): Update. * sol-thread.c (ps_pglobal_lookup): Update. * sol2-tdep.c (sol2_skip_solib_resolver): Update. * solib-dsbt.c (lm_base): Update. * solib-frv.c (lm_base, frv_relocate_section_addresses): Update. * solib-irix.c (locate_base): Update. * solib-som.c (som_solib_create_inferior_hook) (som_solib_desire_dynamic_linker_symbols, link_map_start): Update. * solib-spu.c (spu_enable_break): Update. * solib-svr4.c (elf_locate_base, enable_break): Update. * spu-tdep.c (spu_get_overlay_table, spu_catch_start) (flush_ea_cache): Update. * stabsread.c (define_symbol): Update. * symfile.c (simple_read_overlay_table): Update. * symtab.c (find_pc_sect_line): Update. * tracepoint.c (scope_info): Update. * tui-disasm.c (tui_get_begin_asm_address): Update. * value.c (value_static_field): Update.
742 lines
19 KiB
C
742 lines
19 KiB
C
/* Copyright (C) 2008-2014 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;
|
|
|
|
/* 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 bound_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.minsym == NULL)
|
|
{
|
|
debug ("enable_dec_thread: No __pthread_dbg_symtable");
|
|
return;
|
|
}
|
|
|
|
status = pthreadDebugContextInit (&caller_context, &debug_callbacks,
|
|
(void *) SYMBOL_VALUE_ADDRESS (msym.minsym),
|
|
&debug_context);
|
|
if (status != ESUCCESS)
|
|
{
|
|
debug ("enable_dec_thread: pthreadDebugContextInit -> %d",
|
|
status);
|
|
return;
|
|
}
|
|
|
|
push_target (&dec_thread_ops);
|
|
dec_thread_active = 1;
|
|
|
|
debug ("enable_dec_thread: Thread support enabled.");
|
|
}
|
|
|
|
/* Deactivate thread support. Do nothing if 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);
|
|
}
|
|
|
|
/* Return non-zero if PTID is still alive.
|
|
|
|
Assumes that DEC_THREAD_LIST is up to date. */
|
|
static int
|
|
dec_thread_ptid_is_alive (ptid_t ptid)
|
|
{
|
|
pthreadDebugId_t tid = ptid_get_tid (ptid);
|
|
int i;
|
|
struct dec_thread_info *info;
|
|
|
|
if (tid == 0)
|
|
/* This is the thread corresponding to the process. This ptid
|
|
is always alive until the program exits. */
|
|
return 1;
|
|
|
|
/* Search whether an entry with the same tid exists in the dec-thread
|
|
list of threads. If it does, then the thread is still alive.
|
|
No match found means that the thread must be dead, now. */
|
|
for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info); i++)
|
|
if (info->thread == tid)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* 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 = *count + 1;
|
|
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 = *listp + 1;
|
|
return 0;
|
|
}
|
|
|
|
/* Implement the find_new_thread target_ops method. */
|
|
|
|
static void
|
|
dec_thread_find_new_threads (struct target_ops *ops)
|
|
{
|
|
int i;
|
|
struct dec_thread_info *info;
|
|
|
|
update_dec_thread_list ();
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* Resynchronize the list of threads known by GDB with the actual
|
|
list of threads reported by libpthread_debug. */
|
|
|
|
static void
|
|
resync_thread_list (struct target_ops *ops)
|
|
{
|
|
int i;
|
|
int num_gdb_threads = 0;
|
|
struct thread_info **gdb_thread_list;
|
|
struct thread_info **next_thread_info;
|
|
|
|
/* Add new threads. */
|
|
dec_thread_find_new_threads (ops);
|
|
|
|
/* 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++)
|
|
if (!dec_thread_ptid_is_alive (gdb_thread_list[i]->ptid))
|
|
delete_thread (gdb_thread_list[i]->ptid);
|
|
}
|
|
|
|
/* The "to_detach" method of the dec_thread_ops. */
|
|
|
|
static void
|
|
dec_thread_detach (struct target_ops *ops, const char *args, int from_tty)
|
|
{
|
|
struct target_ops *beneath = find_target_beneath (ops);
|
|
|
|
debug ("dec_thread_detach");
|
|
|
|
disable_dec_thread ();
|
|
beneath->to_detach (beneath, 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 (struct target_ops *ops,
|
|
ptid_t ptid, struct target_waitstatus *status, int options)
|
|
{
|
|
ptid_t active_ptid;
|
|
struct target_ops *beneath = find_target_beneath (ops);
|
|
|
|
debug ("dec_thread_wait");
|
|
|
|
ptid = beneath->to_wait (beneath, ptid, status, options);
|
|
|
|
/* The ptid returned by the target beneath us is the ptid of the process.
|
|
We need to find which thread is currently active and return its ptid. */
|
|
resync_thread_list (ops);
|
|
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_get_regsets: pthreadDebugThdGetReg -> %d", res);
|
|
return -1;
|
|
}
|
|
memcpy (gregset->regs, ®s, sizeof (regs));
|
|
|
|
res = pthreadDebugThdGetFreg (debug_context, tid, &fregs);
|
|
if (res != ESUCCESS)
|
|
{
|
|
debug ("dec_thread_get_regsets: 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 target_ops *ops,
|
|
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 ()))
|
|
{
|
|
struct target_ops *beneath = find_target_beneath (ops);
|
|
|
|
beneath->to_fetch_registers (beneath, 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_set_regsets: pthreadDebugThdSetReg -> %d", res);
|
|
return -1;
|
|
}
|
|
|
|
memcpy (&fregs, fpregset.regs, sizeof (fregs));
|
|
res = pthreadDebugThdSetFreg (debug_context, tid, &fregs);
|
|
if (res != ESUCCESS)
|
|
{
|
|
debug ("dec_thread_set_regsets: 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 target_ops *ops,
|
|
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 ()))
|
|
{
|
|
struct target_ops *beneath = find_target_beneath (ops);
|
|
|
|
beneath->to_store_registers (beneath, 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 (struct target_ops *ops)
|
|
{
|
|
struct target_ops *beneath = find_target_beneath (ops);
|
|
|
|
debug ("dec_thread_mourn_inferior");
|
|
|
|
disable_dec_thread ();
|
|
beneath->to_mourn_inferior (beneath);
|
|
}
|
|
|
|
/* The "to_thread_alive" method of the dec_thread_ops. */
|
|
static int
|
|
dec_thread_thread_alive (struct target_ops *ops, 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 (struct target_ops *ops, ptid_t ptid)
|
|
{
|
|
static char *ret = NULL;
|
|
|
|
if (ptid_get_tid (ptid) == 0)
|
|
{
|
|
struct target_ops *beneath = find_target_beneath (ops);
|
|
|
|
return beneath->to_pid_to_str (beneath, 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 ();
|
|
}
|
|
|
|
/* The "to_get_ada_task_ptid" method of the dec_thread_ops. */
|
|
|
|
static ptid_t
|
|
dec_thread_get_ada_task_ptid (struct target_ops *self, long lwp, long thread)
|
|
{
|
|
int i;
|
|
struct dec_thread_info *info;
|
|
|
|
debug ("dec_thread_get_ada_task_ptid (struct target_ops *self,"
|
|
" lwp=0x%lx, thread=0x%lx)",
|
|
lwp, thread);
|
|
|
|
for (i = 0; VEC_iterate (dec_thread_info_s, dec_thread_list, i, info);
|
|
i++)
|
|
if (info->info.teb == (pthread_t) thread)
|
|
return ptid_build_from_info (*info);
|
|
|
|
warning (_("Could not find thread id from THREAD = 0x%lx"), thread);
|
|
return inferior_ptid;
|
|
}
|
|
|
|
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_find_new_threads = dec_thread_find_new_threads;
|
|
dec_thread_ops.to_pid_to_str = dec_thread_pid_to_str;
|
|
dec_thread_ops.to_stratum = thread_stratum;
|
|
dec_thread_ops.to_get_ada_task_ptid = dec_thread_get_ada_task_ptid;
|
|
dec_thread_ops.to_magic = OPS_MAGIC;
|
|
}
|
|
|
|
void
|
|
_initialize_dec_thread (void)
|
|
{
|
|
init_dec_thread_ops ();
|
|
complete_target_initialization (&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);
|
|
}
|