root/usr/src/lib/libc/inc/tdb_agent.h
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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
 * or http://www.opensolaris.org/os/licensing.
 * 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
 */
/*
 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef _TDB_AGENT_H
#define _TDB_AGENT_H

/*
 * Thread debug agent control structures.
 *
 * This is an implementation-specific header file that is shared
 * between libc and libc_db.  It is NOT a public header file
 * and must never be installed in /usr/include
 */

#include <thread_db.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * The structure containing per-thread event data.
 */
typedef struct {
        td_thr_events_t eventmask;      /* Which events are enabled? */
        td_event_e      eventnum;       /* Most recent enabled event */
        void            *eventdata;     /* Param. for most recent event */
} td_evbuf_t;

#ifdef _SYSCALL32
typedef struct {
        td_thr_events_t eventmask;      /* Which events are enabled? */
        td_event_e      eventnum;       /* Most recent enabled event */
        caddr32_t       eventdata;      /* Param. for most recent event */
} td_evbuf32_t;
#endif /* _SYSCALL32 */


/*
 * All of these structures are constrained to have a size of 48 bytes.
 * This is so that two 8-byte pointers can be inserted at the front to
 * make up a complete tdb_sync_stats_t structure of exactly 64 bytes.
 * The 'type' element of each structure identifies the type of the union,
 * with values from the following defines.
 */

#define TDB_NONE        0
#define TDB_MUTEX       1
#define TDB_COND        2
#define TDB_RWLOCK      3
#define TDB_SEMA        4

typedef struct {
        uint16_t        type;
        uint16_t        unused;
        uint_t          mutex_lock;
        hrtime_t        mutex_hold_time;
        hrtime_t        mutex_sleep_time;
        uint_t          mutex_sleep;
        uint_t          mutex_try;
        uint_t          mutex_try_fail;
        uint_t          mutex_pad[1];
        hrtime_t        mutex_begin_hold;
} tdb_mutex_stats_t;

typedef struct {
        uint16_t        type;
        uint16_t        unused;
        uint_t          cond_wait;
        uint_t          cond_timedwait;
        uint_t          cond_timedwait_timeout;
        hrtime_t        cond_wait_sleep_time;
        hrtime_t        cond_timedwait_sleep_time;
        uint_t          cond_signal;
        uint_t          cond_broadcast;
        uint_t          cond_pad[2];
} tdb_cond_stats_t;

typedef struct {
        uint16_t        type;
        uint16_t        unused;
        uint_t          rw_rdlock;
        /* rw_rdlock_sleep is the reader cv's cond_wait count */
        /* rw_rdlock_sleep_time is the reader cv's cond_wait_sleep_time */
        uint_t          rw_rdlock_try;
        uint_t          rw_rdlock_try_fail;
        uint_t          rw_pad[1];
        uint_t          rw_wrlock;
        /* rw_wrlock_sleep is the writer cv's cond_wait count */
        /* rw_wrlock_sleep_time is the writer cv's cond_wait_sleep_time */
        hrtime_t        rw_wrlock_hold_time;
        uint_t          rw_wrlock_try;
        uint_t          rw_wrlock_try_fail;
        hrtime_t        rw_wrlock_begin_hold;
} tdb_rwlock_stats_t;

typedef struct {
        uint16_t        type;
        uint16_t        unused;
        uint_t          sema_post;
        uint_t          sema_wait;
        uint_t          sema_wait_sleep;
        hrtime_t        sema_wait_sleep_time;
        uint_t          sema_trywait;
        uint_t          sema_trywait_fail;
        uint_t          sema_max_count;
        uint_t          sema_min_count;
        uint_t          sema_pad[2];
} tdb_sema_stats_t;

/*
 * An entry in the sync. object hash table.
 */
typedef struct {
        uint64_t        next;
        uint64_t        sync_addr;
        union {
                uint16_t                type;
                tdb_mutex_stats_t       mutex;
                tdb_cond_stats_t        cond;
                tdb_rwlock_stats_t      rwlock;
                tdb_sema_stats_t        sema;
        } un;
} tdb_sync_stats_t;

/* peg count values at UINT_MAX */
#define tdb_incr(x)     (((x) != UINT_MAX)? (x)++ : 0)

/*
 * The tdb_register_sync variable is set to REGISTER_SYNC_ENABLE by a
 * debugger to enable synchronization object registration.
 * Thereafter, synchronization primitives call tdb_sync_obj_register()
 * to put their synchronization objects in the registration hash table.
 * In this state, the first call to tdb_sync_obj_register() empties the
 * hash table and sets tdb_register_sync to REGISTER_SYNC_ON.
 *
 * The tdb_register_sync variable is set to REGISTER_SYNC_DISABLE by a
 * debugger to disable synchronization object registration.
 * In this state, the first call to tdb_sync_obj_register() empties the
 * hash table and sets tdb_register_sync to REGISTER_SYNC_OFF.
 * Thereafter, synchronization primitives do not call tdb_sync_obj_register().
 *
 * Sync object *_destroy() functions always call tdb_sync_obj_deregister().
 */
typedef uint8_t register_sync_t;
#define REGISTER_SYNC_OFF       0       /* registration is off */
#define REGISTER_SYNC_ON        1       /* registration is on */
#define REGISTER_SYNC_DISABLE   2       /* request to disable registration */
#define REGISTER_SYNC_ENABLE    3       /* request to enable registration */

extern  tdb_sync_stats_t        *tdb_sync_obj_register(void *, int *);
extern  void                    tdb_sync_obj_deregister(void *);

/*
 * Definitions for acquiring pointers to synch object statistics blocks
 * contained in the synchronization object registration hash table.
 */
extern  tdb_mutex_stats_t       *tdb_mutex_stats(mutex_t *);
extern  tdb_cond_stats_t        *tdb_cond_stats(cond_t *);
extern  tdb_rwlock_stats_t      *tdb_rwlock_stats(rwlock_t *);
extern  tdb_sema_stats_t        *tdb_sema_stats(sema_t *);

#define REGISTER_SYNC(udp)      (udp)->uberflags.uf_tdb_register_sync

#define MUTEX_STATS(mp, udp)    \
                (REGISTER_SYNC(udp)? tdb_mutex_stats(mp): NULL)
#define COND_STATS(cvp, udp)    \
                (REGISTER_SYNC(udp)? tdb_cond_stats(cvp): NULL)
#define RWLOCK_STATS(rwlp, udp) \
                (REGISTER_SYNC(udp)? tdb_rwlock_stats(rwlp): NULL)
#define SEMA_STATS(sp, udp)     \
                (REGISTER_SYNC(udp)? tdb_sema_stats(sp): NULL)

/*
 * Parameters of the synchronization object registration hash table.
 */
#define TDB_HASH_SHIFT  15      /* 32K hash table entries */
#define TDB_HASH_SIZE   (1 << TDB_HASH_SHIFT)
#define TDB_HASH_MASK   (TDB_HASH_SIZE - 1)

/*
 * uberdata.tdb_hash_lock protects all synchronization object
 * hash table data structures.
 * uberdata.tdb_hash_lock_stats is a special tdb_sync_stats structure
 * reserved for tdb_hash_lock.
 */

typedef void (*tdb_ev_func_t)(void);

/*
 * Uberdata for thread debug interfaces (known to libc_db).
 */
typedef struct {
        /*
         * Pointer to the hash table of sync_addr_t descriptors.
         * This holds the addresses of all of the synchronization variables
         * that the library has seen since tracking was enabled by a debugger.
         */
        uint64_t                *tdb_sync_addr_hash;
        /*
         * The number of entries in the hash table.
         */
        uint_t                  tdb_register_count;
        int                     tdb_hash_alloc_failed;
        /*
         * The free list of sync_addr_t descriptors.
         * When the free list is used up, it is replenished using mmap().
         * sync_addr_t descriptors are never freed, though they may be
         * removed from the hash table and returned to the free list.
         */
        tdb_sync_stats_t        *tdb_sync_addr_free;
        tdb_sync_stats_t        *tdb_sync_addr_last;
        size_t                  tdb_sync_alloc;
        /*
         * The set of globally enabled events to report to libc_db.
         */
        td_thr_events_t         tdb_ev_global_mask;
        /*
         * The array of event function pointers.
         */
        const tdb_ev_func_t     *tdb_events;
} tdb_t;

#ifdef _SYSCALL32
typedef struct {
        caddr32_t       tdb_sync_addr_hash;
        uint_t          tdb_register_count;
        int             tdb_hash_alloc_failed;
        caddr32_t       tdb_sync_addr_free;
        caddr32_t       tdb_sync_addr_last;
        size32_t        tdb_sync_alloc;
        td_thr_events_t tdb_ev_global_mask;
        caddr32_t       tdb_events;
} tdb32_t;
#endif /* _SYSCALL32 */

/*
 * This will have to change if event numbers exceed 31.
 * Note that we only test tdb_ev_global_mask.event_bits[0] below.
 */
#define __td_event_report(ulwp, event, udp)                             \
        (((ulwp)->ul_td_events_enable &&                                \
        td_eventismember(&(ulwp)->ul_td_evbuf.eventmask, (event))) ||   \
        ((udp)->tdb.tdb_ev_global_mask.event_bits[0] &&                 \
        td_eventismember(&(udp)->tdb.tdb_ev_global_mask, (event))))

/*
 * Event "reporting" functions.  A thread reports an event by calling
 * one of these empty functions; a debugger can set a breakpoint
 * at the address of any of these functions to determine that an
 * event is being reported.
 */
extern const tdb_ev_func_t tdb_events[TD_MAX_EVENT_NUM - TD_MIN_EVENT_NUM + 1];

#define tdb_event(event, udp)           \
        (*(udp)->tdb.tdb_events[(event) - TD_MIN_EVENT_NUM])()

#ifdef __cplusplus
}
#endif

#endif  /* _TDB_AGENT_H */