/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <thr_uberdata.h>
#include <thread_db.h>
#include <libc_int.h>
/*
* Private structures.
*/
typedef union {
} td_so_un_t;
struct td_thragent {
int initialized;
int sync_tracking;
int model;
int primary_map;
int hash_size;
};
/*
* This is the name of the variable in libc that contains
* the uberdata address that we will need.
*/
/*
* This is the actual name of uberdata, used in the event
* that tdb_bootstrap has not yet been initialized.
*/
/*
* The library name should end with ".so.1", but older versions of
* dbx expect the unadorned name and malfunction if ".1" is specified.
* Unfortunately, if ".1" is not specified, mdb malfunctions when it
* is applied to another instance of itself (due to the presence of
*/
/*
* Initialize threads debugging interface.
*/
{
return (TD_OK);
}
/*
* This function does nothing, and never did.
* But the symbol is in the ABI, so we can't delete it.
*/
void
__td_log()
{
}
/*
* Short-cut to read just the hash table size from the process,
* to avoid repeatedly reading the full uberdata structure when
* dealing with a single-threaded process.
*/
static uint_t
{
switch (ta_p->initialized) {
default: /* uninitialized */
return (0);
case 1: /* partially initialized */
break;
case 2: /* fully initialized */
}
} else {
#if defined(_LP64) && defined(_SYSCALL32)
#else
addr = 0;
#endif
}
!= PS_OK)
return (0);
return (hash_size);
}
static td_err_e
{
return (TD_DBERR);
return (TD_DBERR);
} else {
#if defined(_LP64) && defined(_SYSCALL32)
int i;
return (TD_DBERR);
return (TD_DBERR);
#else
return (TD_DBERR);
#endif
}
ta_p->single_lwpid = 0;
} else { /* single-threaded */
/*
* It may not be ulwp_one if this is a child of fork1().
*/
return (TD_DBERR);
ta_p->initialized = 0;
return (TD_DBERR);
} else {
#if defined(_LP64) && defined(_SYSCALL32)
return (TD_DBERR);
ta_p->initialized = 0;
return (TD_DBERR);
#else
return (TD_DBERR);
#endif
}
}
if (!ta_p->primary_map)
ta_p->initialized = 0;
return (TD_OK);
}
static td_err_e
{
int do_1;
switch (ta_p->initialized) {
case 2: /* fully initialized */
return (TD_OK);
case 1: /* partially initialized */
return (TD_OK);
return (td_read_uberdata(ta_p));
}
/*
* Uninitialized -- do the startup work.
* We set ta_p->initialized to -1 to cut off recursive calls
* into libc_db by code in the provider of ps_pglobal_lookup().
*/
do_1 = 0;
do_1 = 1;
}
return (TD_NOLIBTHREAD);
return (TD_ERR);
return (TD_NOLIBTHREAD);
return (TD_ERR);
/*
* Read the uberdata address into the thread agent structure.
*/
return (TD_DBERR);
return (TD_DBERR);
/* primary linkmap in the tgt is not initialized */
}
} else {
#if defined(_LP64) && defined(_SYSCALL32)
return (TD_DBERR);
return (TD_DBERR);
/* primary linkmap in the tgt is not initialized */
}
#else
return (TD_DBERR);
#endif /* _SYSCALL32 */
}
return (return_val);
ta_p->initialized = 0;
return (TD_OK);
}
/*
* Allocate a new agent process handle ("thread agent").
*/
{
int model;
return (TD_BADPH);
return (TD_ERR);
return (TD_DBERR);
/*
* ps_pdmodel might not be defined if this is an older client.
* Make it a weak symbol and test if it exists before calling.
*/
if (ps_pdmodel == NULL) {
(void) ps_pcontinue(ph_p);
return (TD_ERR);
}
(void) ps_pcontinue(ph_p);
return (TD_MALLOC);
}
/*
* Initialize the agent process handle.
* Pick up the symbol value we need from the target process.
*/
/*
* Because the old libthread_db enabled lock tracking by default,
* we must also do it. However, we do it only if the application
* provides the ps_kill() and ps_lrolltoaddr() interfaces.
* (dbx provides the ps_kill() and ps_lrolltoaddr() interfaces.)
*/
else if (oldenable != REGISTER_SYNC_OFF ||
/*
* Lock tracking was already enabled or we
* failed to enable it, probably because we
* are examining a core file. In either case
* set the sync_tracking flag non-zero to
* indicate that we should not attempt to
* disable lock tracking when we delete the
* agent process handle in td_ta_delete().
*/
}
}
if (return_val == TD_OK)
else
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Utility function to grab the readers lock and return the prochandle,
* given an agent process handle. Performs standard error checking.
* Returns non-NULL with the lock held, or NULL with the lock not held.
*/
static struct ps_prochandle *
{
} else {
}
return (ph_p);
}
/*
* Utility function to grab the readers lock and return the prochandle,
* given an agent thread handle. Performs standard error checking.
* Returns non-NULL with the lock held, or NULL with the lock not held.
*/
static struct ps_prochandle *
{
return (NULL);
}
}
/*
* Utility function to grab the readers lock and return the prochandle,
* given a synchronization object handle. Performs standard error checking.
* Returns non-NULL with the lock held, or NULL with the lock not held.
*/
static struct ps_prochandle *
{
return (NULL);
}
}
/*
* Unlock the agent process handle obtained from ph_lock_*().
*/
static void
{
}
/*
* De-allocate an agent process handle,
* releasing all related resources.
*
* XXX -- This is hopelessly broken ---
* Storage for thread agent is not deallocated. The prochandle
* in the thread agent is set to NULL so that future uses of
* the thread agent can be detected and an error value returned.
* All functions in the external user interface that make
* use of the thread agent are expected
* to check for a NULL prochandle in the thread agent.
* All such functions are also expected to obtain a
* reader lock on the thread agent while it is using it.
*/
{
/*
* This is the only place we grab the writer lock.
* We are going to NULL out the prochandle.
*/
return (TD_BADTA);
return (TD_BADPH);
}
/*
* If synch. tracking was disabled when td_ta_new() was called and
* if td_ta_sync_tracking_enable() was never called, then disable
* synch. tracking (it was enabled by default in td_ta_new()).
*/
if (ta_p->sync_tracking == 0 &&
}
return (TD_OK);
}
/*
* Map an agent process handle to a client prochandle.
* Currently unused by dbx.
*/
{
return (TD_ERR);
return (return_val);
return (TD_OK);
}
/*
* Set the process's suggested concurrency level.
* This is a no-op in a one-level model.
* Currently unused by dbx.
*/
/* ARGSUSED1 */
{
return (TD_BADTA);
return (TD_BADPH);
return (TD_OK);
}
/*
* Get the number of threads in the process.
*/
{
int nthreads;
int nzombies;
} else {
#if defined(_LP64) && defined(_SYSCALL32)
#else
nthreads_addr = 0;
nzombies_addr = 0;
#endif /* _SYSCALL32 */
}
return (TD_ERR);
return (return_val);
if (return_val == TD_OK)
return (return_val);
}
typedef struct {
int found;
/*
* Check the value in data against the thread id.
* If it matches, return 1 to terminate iterations.
* This function is used by td_ta_map_id2thr() to map a tid to a thread handle.
*/
static int
{
return (1);
}
return (0);
}
/*
* Given a thread identifier, return the corresponding thread handle.
*/
{
return (TD_NOTHR);
return (TD_OK);
}
/*
* LOCKING EXCEPTION - Locking is not required here because
* the locking and checking will be done in __td_ta_thr_iter.
*/
return (TD_BADTA);
return (TD_BADTH);
if (tid == 0)
return (TD_NOTHR);
if (return_val == TD_OK) {
else
}
return (return_val);
}
/*
* Map the address of a synchronization object to a sync. object handle.
*/
{
return (TD_BADSH);
return (TD_ERR);
return (return_val);
/*
* Check the magic number of the sync. object to make sure it's valid.
* The magic number is at the same offset for all sync. objects.
*/
return (TD_BADSH);
}
return (TD_BADSH);
/*
* Just fill in the appropriate fields of the sync. handle.
*/
return (TD_OK);
}
/*
* Iterate over the set of global TSD keys.
* The call back function is called with three arguments,
* a key, a pointer to the destructor function, and the cbdata pointer.
* Currently unused by dbx.
*/
{
int key;
int numkeys;
return (TD_ERR);
return (return_val);
return (TD_DBERR);
}
else {
if (numkeys > 0)
}
} else {
#if defined(_LP64) && defined(_SYSCALL32)
else {
if (numkeys > 0)
}
#else
#endif /* _SYSCALL32 */
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
if (destructors == NULL)
else {
if (destructor != TSD_UNALLOCATED &&
break;
}
}
#if defined(_LP64) && defined(_SYSCALL32)
} else {
else {
cbdata_p))
break;
}
}
#endif /* _SYSCALL32 */
}
if (destructors)
(void) ps_pcontinue(ph_p);
return (return_val);
}
int
{
return (
}
/*
* Description:
* Iterate over all threads. For each thread call
* the function pointed to by "cb" with a pointer
* to a thread handle, and a pointer to data which
* can be NULL. Only call td_thr_iter_f() on threads
* which match the properties of state, ti_pri,
* ti_sigmask_p, and ti_user_flags. If cb returns
* a non-zero value, terminate iterations.
*
* Input:
* *ta_p - thread agent
* *cb - call back function defined by user.
* td_thr_iter_f() takes a thread handle and
* cbdata_p as a parameter.
* cbdata_p - parameter for td_thr_iter_f().
*
* state - state of threads of interest. A value of
* TD_THR_ANY_STATE from enum td_thr_state_e
* does not restrict iterations by state.
* ti_pri - lower bound of priorities of threads of
* interest. A value of TD_THR_LOWEST_PRIORITY
* defined in thread_db.h does not restrict
* iterations by priority. A thread with priority
* less than ti_pri will NOT be passed to the callback
* function.
* ti_sigmask_p - signal mask of threads of interest.
* A value of TD_SIGNO_MASK defined in thread_db.h
* does not restrict iterations by signal mask.
* ti_user_flags - user flags of threads of interest. A
* value of TD_THR_ANY_USER_FLAGS defined in thread_db.h
* does not restrict iterations by user flags.
*/
{
return (TD_ERR);
/*
* If state is not within bound, short circuit.
*/
return (TD_OK);
return (return_val);
return (TD_DBERR);
}
/*
* For each ulwp_t in the circular linked lists pointed
* to by "all_lwps" and "all_zombies":
* (1) Filter each thread.
* (2) Create the thread_object for each thread that passes.
* (3) Call the call back function on each thread.
*/
&first_lwp_addr, sizeof (first_lwp_addr));
&first_zombie_addr, sizeof (first_zombie_addr));
} else {
#if defined(_LP64) && defined(_SYSCALL32)
#else /* _SYSCALL32 */
db_return2 = PS_ERR;
#endif /* _SYSCALL32 */
}
/*
* If first_lwp_addr and first_zombie_addr are both NULL,
* libc must not yet be initialized or all threads have
* exited. Return TD_NOTHR and all will be well.
*/
(void) ps_pcontinue(ph_p);
return (TD_NOTHR);
}
(void) ps_pcontinue(ph_p);
return (TD_DBERR);
}
/*
* Run down the lists of all living and dead lwps.
*/
if (first_lwp_addr == NULL)
for (;;) {
int userpri;
unsigned userflags;
/*
* Read the ulwp struct.
*/
break;
}
(void) sigemptyset(&mask);
else
} else {
#if defined(_LP64) && defined(_SYSCALL32)
break;
}
(void) sigemptyset(&mask);
else
#else /* _SYSCALL32 */
return_val = TD_ERR;
break;
#endif /* _SYSCALL32 */
}
/*
* Filter on state, priority, sigmask, and user flags.
*/
(state != TD_THR_ANY_STATE))
goto advance;
goto advance;
if (ti_sigmask_p != TD_SIGNO_MASK &&
goto advance;
if (ti_user_flags != userflags &&
ti_user_flags != (unsigned)TD_THR_ANY_USER_FLAGS)
goto advance;
/*
* Call back - break if the return
* from the call back is non-zero.
*/
break;
/*
* Switch to the zombie list, unless it is NULL
* or we have already been doing the zombie list,
* in which case terminate the loop.
*/
if (first_zombie_addr == NULL ||
break;
}
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Enable or disable process synchronization object tracking.
* Currently unused by dbx.
*/
{
return (return_val);
/*
* Values of tdb_register_sync in the victim process:
* REGISTER_SYNC_ENABLE enables registration of synch objects
* REGISTER_SYNC_DISABLE disables registration of synch objects
* These cause the table to be cleared and tdb_register_sync set to:
* REGISTER_SYNC_ON registration in effect
* REGISTER_SYNC_OFF registration not in effect
*/
/*
* Remember that this interface was called (see td_ta_delete()).
*/
return (return_val);
}
/*
* Iterate over all known synchronization variables.
* It is very possible that the list generated is incomplete,
* because the iterator can only find synchronization variables
* that have been registered by the process since synchronization
* object registration was enabled.
* The call back function cb is called for each synchronization
* variable with two arguments: a pointer to the synchronization
* handle and the passed-in argument cbdata.
* If cb returns a non-zero value, iterations are terminated.
*/
{
int i;
void *vaddr;
return (TD_ERR);
return (return_val);
return (TD_DBERR);
}
goto out;
}
if (enable != REGISTER_SYNC_ON)
goto out;
/*
* First read the hash table.
* The hash table is large; allocate with mmap().
*/
== MAP_FAILED) {
goto out;
}
goto out;
}
} else {
#ifdef _SYSCALL32
goto out;
}
#else
return_val = TD_ERR;
goto out;
#endif /* _SYSCALL32 */
}
goto out;
goto out;
}
/*
* Now scan the hash table.
*/
for (i = 0; i < TDB_HASH_SIZE; i++) {
goto out;
}
/* not registered since registration enabled */
continue;
}
goto out;
}
}
out:
if (sync_addr_hash != NULL)
(void) munmap((void *)sync_addr_hash,
TDB_HASH_SIZE * sizeof (uint64_t));
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Enable process statistics collection.
*/
/* ARGSUSED */
{
return (TD_NOCAPAB);
}
/*
* Reset process statistics.
*/
/* ARGSUSED */
{
return (TD_NOCAPAB);
}
/*
* Read process statistics.
*/
/* ARGSUSED */
{
return (TD_NOCAPAB);
}
/*
* Transfer information from lwp struct to thread information struct.
* XXX -- lots of this needs cleaning up.
*/
static void
{
lwpid = 1;
/*
* The bloody fools got this backwards!
*/
}
REPLACEMENT_SIZE : sizeof (ulwp_t);
ti_p->ti_db_suspended = 0;
ti_p->ti_traceme = 0;
ti_p->ti_preemptflag = 0;
ti_p->ti_pirecflag = 0;
}
#if defined(_LP64) && defined(_SYSCALL32)
static void
{
lwpid = 1;
/*
* The bloody fools got this backwards!
*/
}
REPLACEMENT_SIZE32 : sizeof (ulwp32_t);
ti_p->ti_db_suspended = 0;
ti_p->ti_traceme = 0;
ti_p->ti_preemptflag = 0;
ti_p->ti_pirecflag = 0;
}
#endif /* _SYSCALL32 */
/*
* Get thread information.
*/
{
return (TD_ERR);
return (return_val);
return (TD_DBERR);
}
/*
* Read the ulwp struct from the process.
* Transfer the ulwp struct to the thread information struct.
*/
else
} else {
#if defined(_LP64) && defined(_SYSCALL32)
else
#else
return_val = TD_ERR;
#endif /* _SYSCALL32 */
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Given a process and an event number, return information about
* an address in the process or at which a breakpoint can be set
* to monitor the event.
*/
{
return (TD_BADTA);
return (TD_NOEVENT);
return (TD_ERR);
return (TD_OK);
}
/*
* Add the events in eventset 2 to eventset 1.
*/
static void
{
int i;
for (i = 0; i < TD_EVENTSIZE; i++)
}
/*
* Delete the events in eventset 2 from eventset 1.
*/
static void
{
int i;
for (i = 0; i < TD_EVENTSIZE; i++)
}
/*
* Either add or delete the given event set from a thread's event mask.
*/
static td_err_e
{
char enable;
return (return_val);
} else {
#if defined(_LP64) && defined(_SYSCALL32)
#else
return (TD_ERR);
#endif /* _SYSCALL32 */
}
return (TD_DBERR);
}
else {
if (onoff)
else
!= PS_OK)
else {
enable = 0;
enable = 1;
}
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Enable or disable tracing for a given thread. Tracing
* is filtered based on the event mask of each thread. Tracing
* event mask.
* Currently unused by dbx.
*/
{
}
/*
* Set event mask to enable event. event is turned on in
* event mask for thread. If a thread encounters an event
* for which its event mask is on, notification will be sent
* to the debugger.
* Addresses for each event are provided to the
* debugger. It is assumed that a breakpoint of some type will
* be placed at that address. If the event mask for the thread
* is on, the instruction at the address will be executed.
* Otherwise, the instruction will be skipped.
*/
{
}
/*
* Enable or disable a set of events in the process-global event mask,
* depending on the value of onoff.
*/
static td_err_e
{
return (return_val);
return (TD_DBERR);
}
else {
if (onoff)
else
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Enable a set of events in the process-global event mask.
*/
{
}
/*
* Set event mask to disable the given event set; these events are cleared
* from the event mask of the thread. Events that occur for a thread
* with the event masked off will not cause notification to be
* sent to the debugger (see td_thr_set_event for fuller description).
*/
{
}
/*
* Disable a set of events in the process-global event mask.
*/
{
}
/*
* This function returns the most recent event message, if any,
* associated with a thread. Given a thread handle, return the message
* corresponding to the event encountered by the thread. Only one
* message per thread is saved. Messages from earlier events are lost
* when later events occur.
*/
{
return (return_val);
return (TD_BADTA);
}
} else {
/* "Consume" the message */
!= PS_OK)
}
} else {
#if defined(_LP64) && defined(_SYSCALL32)
} else {
/* "Consume" the message */
!= PS_OK)
}
#else
return_val = TD_ERR;
#endif /* _SYSCALL32 */
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* The callback function td_ta_event_getmsg uses when looking for
* a thread with an event. A thin wrapper around td_thr_event_getmsg.
*/
static int
{
/*
* Got an event, stop iterating.
*
* Because of past mistakes in interface definition,
* we are forced to pass back a static local variable
* for the thread handle because th_p is a pointer
* to a local variable in __td_ta_thr_iter().
* Grr...
*/
return (1);
}
return (0);
}
/*
* This function is just like td_thr_event_getmsg, except that it is
* passed a process handle rather than a thread handle, and returns
* an event message for some thread in the process that has an event
* message pending. If no thread has an event message pending, this
* routine returns TD_NOEVENT. Thus, all pending event messages may
* be collected from a process by repeatedly calling this routine
* until it returns TD_NOEVENT.
*/
{
return (TD_BADTA);
return (TD_BADPH);
return (TD_ERR);
TD_THR_ANY_USER_FLAGS)) != TD_OK)
return (return_val);
return (TD_NOEVENT);
return (TD_OK);
}
static lwpid_t
{
/*
* The caller holds the prochandle lock
* and has already verfied everything.
*/
lwpid = 0;
else if (lwpid == 0)
lwpid = 1;
} else {
#if defined(_LP64) && defined(_SYSCALL32)
lwpid = 0;
else if (lwpid == 0)
lwpid = 1;
#else
lwpid = 0;
#endif /* _SYSCALL32 */
}
return (lwpid);
}
/*
* Suspend a thread.
* XXX: What does this mean in a one-level model?
*/
{
return (return_val);
return (return_val);
}
/*
* Resume a suspended thread.
* XXX: What does this mean in a one-level model?
*/
{
return (return_val);
return (return_val);
}
/*
* Set a thread's signal mask.
* Currently unused by dbx.
*/
/* ARGSUSED */
{
return (TD_NOCAPAB);
}
/*
* Set a thread's "signals-pending" set.
* Currently unused by dbx.
*/
/* ARGSUSED */
{
return (TD_NOCAPAB);
}
/*
* Get a thread's general register set.
*/
{
return (return_val);
return (TD_DBERR);
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Set a thread's general register set.
*/
{
return (return_val);
return (TD_DBERR);
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Get a thread's floating-point register set.
*/
{
return (return_val);
return (TD_DBERR);
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Set a thread's floating-point register set.
*/
{
return (return_val);
return (TD_DBERR);
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Get the size of the extra state register set for this architecture.
* Currently unused by dbx.
*/
/* ARGSUSED */
{
return (return_val);
return (TD_DBERR);
}
if (err == PS_NOXREGS) {
} else {
}
#else
#endif
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Get a thread's extra state register set.
*/
/* ARGSUSED */
{
return (return_val);
return (TD_DBERR);
}
if (err == PS_NOXREGS) {
} else {
}
#else
#endif
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Set a thread's extra state register set.
*/
/* ARGSUSED */
{
return (return_val);
return (TD_DBERR);
}
if (err == PS_NOXREGS) {
} else {
}
#else
#endif
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Get the size of the extra state register set for this processor.
* Currently unused by dbx.
*/
/* ARGSUSED */
{
#if defined(__sparc)
return (return_val);
return (TD_DBERR);
}
/*
* ps_lgetcxregsize might not be defined if this is an older client.
* Make it a weak symbol and test if it exists before calling.
*/
if (ps_lgetcxregsize == NULL) {
err = PS_NOCXREGS;
} else {
}
if (err == PS_NOCXREGS) {
} else {
}
}
(void) ps_pcontinue(ph_p);
#else
#endif
return (return_val);
}
/*
* Get a thread's extra state register set.
*/
/* ARGSUSED */
{
#if defined(__sparc)
return (return_val);
return (TD_DBERR);
}
/*
* ps_lgetcxregs might not be defined if this is an older client.
* Make it a weak symbol and test if it exists before calling.
*/
if (ps_lgetcxregs == NULL) {
err = PS_NOCXREGS;
} else {
}
if (err == PS_NOCXREGS) {
} else {
}
}
(void) ps_pcontinue(ph_p);
#else
#endif
return (return_val);
}
/*
* Set a thread's extra state register set.
*/
/* ARGSUSED */
{
#if defined(__sparc)
return (return_val);
return (TD_DBERR);
}
/*
* ps_lsetcxregs might not be defined if this is an older client.
* Make it a weak symbol and test if it exists before calling.
*/
if (ps_lsetcxregs == NULL) {
err = PS_NOCXREGS;
} else {
}
if (err == PS_NOCXREGS) {
} else {
}
}
(void) ps_pcontinue(ph_p);
#else
#endif
return (return_val);
}
struct searcher {
int status;
};
/*
* Check the struct thread address in *th_p again first
* value in "data". If value in data is found, set second value
* in "data" to 1 and return 1 to terminate iterations.
* This function is used by td_thr_validate() to verify that
* a thread handle is valid.
*/
static int
{
return (1);
}
return (0);
}
/*
* Validate the thread handle. Check that
* corresponds to thread with handle *th_p.
* Currently unused by dbx.
*/
{
return (TD_BADTH);
return (TD_BADTH);
/*
* LOCKING EXCEPTION - Locking is not required
* here because no use of the thread agent is made (other
* than the sanity check) and checking of the thread
* agent will be done in __td_ta_thr_iter.
*/
return (return_val);
}
/*
* Get a thread's private binding to a given thread specific
* data(TSD) key(see thr_getspecific(3T). If the thread doesn't
* have a binding for a particular key, then NULL is returned.
*/
{
int maxkey;
int nkey;
return (TD_ERR);
return (return_val);
return (TD_DBERR);
}
else {
}
} else {
#if defined(_LP64) && defined(_SYSCALL32)
else {
} else {
}
}
#else
return_val = TD_ERR;
#endif /* _SYSCALL32 */
}
/* NULL has already been stored in data_pp */
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Read the value from the thread's tsd array.
*/
void *value;
else
#if defined(_LP64) && defined(_SYSCALL32)
} else {
else
#endif /* _SYSCALL32 */
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Get the base address of a thread's thread local storage (TLS) block
* for the module (executable or shared object) identified by 'moduleid'.
*/
{
return (TD_ERR);
return (return_val);
return (TD_DBERR);
}
(psaddr_t)((TLS_modinfo *)
else
} else {
#if defined(_LP64) && defined(_SYSCALL32)
(psaddr_t)((TLS_modinfo32 *)
else
#else
return_val = TD_ERR;
#endif /* _SYSCALL32 */
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Change a thread's priority to the value specified by ti_pri.
* Currently unused by dbx.
*/
/* ARGSUSED */
{
return (TD_NOCAPAB);
}
/*
* This structure links td_thr_lockowner and the lowner_cb callback function.
*/
typedef struct {
void *owner_cb_arg;
static int
{
int trunc = 0;
union {
} rw_m;
trunc = 1;
return (0);
}
}
return (0);
}
/*
* Iterate over the set of locks owned by a specified thread.
* If cb returns a non-zero value, terminate iterations.
*/
void *cb_data)
{
/*
* Just sanity checks.
*/
return (return_val);
}
/*
* If a thread is asleep on a synchronization variable,
* then get the synchronization handle.
*/
{
return (TD_ERR);
return (return_val);
/*
* No need to stop the process for a simple read.
*/
} else {
#if defined(_LP64) && defined(_SYSCALL32)
#else
return_val = TD_ERR;
#endif /* _SYSCALL32 */
}
if (return_val == TD_OK)
return_val = TD_ERR;
} else {
}
return (return_val);
}
/*
* Which thread is running on an lwp?
*/
{
}
/*
* Common code for td_sync_get_info() and td_sync_get_stats()
*/
static td_err_e
{
int trunc = 0;
/*
* Determine the sync. object type; a little type fudgery here.
* First attempt to read the whole union. If that fails, attempt
* to read just the condvar. A condvar is the smallest sync. object.
*/
trunc = 1;
return (TD_DBERR);
}
case MUTEX_MAGIC:
return (TD_DBERR);
si_p->si_ownerpid =
}
break;
case COND_MAGIC:
break;
case SEMA_MAGIC:
!= PS_OK)
return (TD_DBERR);
/* this is useless but the old interface provided it */
break;
case RWL_MAGIC:
{
return (TD_DBERR);
if (rwstate & URW_WRITE_LOCKED) {
si_p->si_ownerpid =
} else {
}
/* this is useless but the old interface provided it */
break;
}
default:
return (TD_BADSH);
}
return (TD_OK);
}
/*
* Given a synchronization handle, fill in the
* information for the synchronization variable into *si_p.
*/
{
return (TD_ERR);
return (return_val);
return (TD_DBERR);
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
static uint_t
{
}
static uint_t
{
}
static td_err_e
{
/*
* Compute the hash table index from the synch object's address.
*/
else
/*
* Get the address of the first element in the linked list.
*/
return (TD_DBERR);
/*
* Search the linked list for an entry for the synch object..
*/
return (TD_DBERR);
return (TD_OK);
}
return (TD_OK);
}
/*
* Given a synchronization handle, fill in the
* statistics for the synchronization variable into *ss_p.
*/
{
return (TD_ERR);
return (return_val);
return (TD_DBERR);
}
!= TD_OK) {
if (return_val != TD_BADSH)
goto out;
/* we can correct TD_BADSH */
/* we correct si_type and si_size below */
return_val = TD_OK;
}
goto out;
}
if (enable != REGISTER_SYNC_ON)
goto out;
/*
* Get the address of the hash table in the target process.
*/
goto out;
}
} else {
#if defined(_LP64) && defined(_SYSCALL32)
goto out;
}
#else
return_val = TD_ERR;
goto out;
#endif /* _SYSCALL32 */
}
if (hashaddr == 0)
else
if (return_val != TD_OK)
goto out;
/*
* We have the hash table entry. Transfer the data to
* the td_syncstats_t structure provided by the caller.
*/
case TDB_MUTEX:
{
msp->mutex_lock =
msp->mutex_sleep =
break;
}
case TDB_COND:
{
csp->cond_signal =
csp->cond_internal =
break;
}
case TDB_RWLOCK:
{
break;
}
case TDB_SEMA:
{
ssp->sema_trywait =
break;
}
default:
break;
}
out:
(void) ps_pcontinue(ph_p);
return (return_val);
}
/*
* Change the state of a synchronization variable.
* 1) mutex lock state set to value
* 2) semaphore's count set to value
* 3) writer's lock set by value < 0
* 4) reader's lock number of readers set to value >= 0
* Currently unused by dbx.
*/
{
int trunc = 0;
return (return_val);
return (TD_DBERR);
}
/*
* Read the synch. variable information.
* First attempt to read the whole union and if that fails
* fall back to reading only the smallest member, the condvar.
*/
sizeof (generic_so)) != PS_OK) {
trunc = 1;
(void) ps_pcontinue(ph_p);
return (TD_DBERR);
}
}
/*
* Set the new value in the sync. variable, read the synch. variable
* information. from the process, reset its value and write it back.
*/
case MUTEX_MAGIC:
break;
}
break;
case SEMA_MAGIC:
!= PS_OK) {
break;
}
break;
case COND_MAGIC:
/* Operation not supported on a condition variable */
return_val = TD_ERR;
break;
case RWL_MAGIC:
break;
}
*rwstate &= URW_HAS_WAITERS;
if (value < 0)
*rwstate |= URW_WRITE_LOCKED;
else
break;
default:
/* Bad sync. object type */
break;
}
(void) ps_pcontinue(ph_p);
return (return_val);
}
typedef struct {
void *waiter_cb_arg;
static int
{
return (1);
}
} else {
#if defined(_LP64) && defined(_SYSCALL32)
return (1);
}
#else
return (1);
#endif /* _SYSCALL32 */
}
return (0);
return (0);
}
/*
* For a given synchronization variable, iterate over the
* set of waiting threads. The call back function is passed
* two parameters, a pointer to a thread handle and a pointer
* to extra call back data.
*/
{
return (return_val);
return (TD_DBERR);
}
switch (wcb.sync_magic) {
case MUTEX_MAGIC:
case COND_MAGIC:
case SEMA_MAGIC:
case RWL_MAGIC:
break;
default:
return (TD_BADSH);
}
if (return_val != TD_OK)
return (return_val);
}