/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * @OSF_COPYRIGHT@
 * 
 */
/*
 * File:  etap.c
 */

#include <cpus.h>
#include <kern/lock.h>
#include <kern/etap_macros.h>
#include <kern/misc_protos.h>
#include <kern/host.h>
#include <types.h>
#include <mach/kern_return.h>
#include <mach/port.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>         /* for kernel_map, ipc_kernel_map */
#if ETAP_MONITOR
#include <machine/machine_tables.h>
#include <mach/clock.h>
#include <mach/clock_reply.h>
#include <mach/default_pager_object.h>
#include <device/device.h>
#include <device/device_reply.h>
#include <device/device_request.h>
#include <mach_debug/mach_debug.h>
/*#include <mach/mach_host.h>*/
#include <mach/mach_norma.h>
#include <mach/mach_port.h>
#include <mach/memory_object_default.h>
#include <mach/memory_object_user.h>
#include <mach/notify_server.h>
#include <mach/prof.h>
#include <machine/unix_map.h>
#endif
#if MACH_KDB
#include <ddb/db_output.h>
#include <ddb/db_sym.h>
#include <ddb/db_command.h>
#if 0 /* WHY?? */
#include <i386/ipl.h>
#endif
#endif

/*
 *	Forwards
 */

kern_return_t
etap_get_info(host_priv_t, int*, int*, vm_offset_t*, vm_offset_t*,
	      int*, int*, int*, int*);

kern_return_t
etap_mon_reconfig(host_priv_t, int);

kern_return_t
etap_new_probe(host_priv_t, vm_address_t, vm_size_t, boolean_t, vm_address_t);

kern_return_t
etap_trace_thread(thread_act_t, boolean_t);

void
etap_trace_reset(int);

void
etap_interrupt_probe(int, int);

void
etap_machcall_probe1(int);

void
etap_machcall_probe2(void);

void
etap_print(void);


#if	ETAP

#ifndef max
#define max(x,y) ((x > y) ? x : y)
#endif  /* max */

event_table_t
etap_event_table_find(etap_event_t);

/* =======================
 *  ETAP Lock definitions
 * =======================
 */

#if	ETAP_LOCK_TRACE
#define	etap_lock	simple_lock_no_trace
#define	etap_unlock	simple_unlock_no_trace
#else	/* ETAP_LOCK_TRACE */
#define etap_lock	simple_lock
#define etap_unlock	simple_unlock
#endif	/* ETAP_LOCK_TRACE */
 
#define	event_table_lock()	etap_lock(&event_table_lock)
#define event_table_unlock()	etap_unlock(&event_table_lock)

#define cumulative_buffer_lock(s)				\
MACRO_BEGIN							\
        s = splhigh();						\
        etap_lock(&cbuff_lock);	 				\
MACRO_END

#define cumulative_buffer_unlock(s)				\
MACRO_BEGIN                                     		\
	etap_unlock(&cbuff_lock);				\
	splx(s);						\
MACRO_END


#if    ETAP_LOCK_ACCUMULATE

/* ========================================
 *  ETAP Cumulative lock trace definitions
 * ========================================
 */

int cbuff_width = ETAP_CBUFF_WIDTH;

/*
 *  Cumulative buffer declaration
 *
 *  For both protection and mapping purposes, the cumulative
 *  buffer must be aligned on a page boundary.  Since the cumulative
 *  buffer must be statically defined, page boundary alignment is not
 *  garenteed.  Instead, the buffer is allocated with 2 extra pages.
 *  The cumulative buffer pointer will round up to the nearest page.
 *
 *  This will garentee page boundary alignment.
 */

#define TWO_PAGES 16384				    /* XXX does this apply ??*/	
#define CBUFF_ALLOCATED_SIZE sizeof(struct cumulative_buffer)+TWO_PAGES

decl_simple_lock_data	(,cbuff_lock)
#if MACH_LDEBUG
simple_lock_t	     	cbuff_locks;
#else
simple_lock_data_t	cbuff_locks;
#endif
char                    cbuff_allocated [CBUFF_ALLOCATED_SIZE];
cumulative_buffer_t	cbuff        = {0};

#endif /* ETAP_LOCK_ACCUMULATE */

#if    ETAP_MONITOR

int mbuff_entries = ETAP_MBUFF_ENTRIES;

/*
 *  Create an array of pointers to monitor buffers.
 *  The buffers themselves are allocated at run-time.
 */

struct monitor_buffer *mbuff[NCPUS];
#endif /* ETAP_MONITOR */

/* ==========================
 *  Event table declarations
 * ==========================
 */

decl_simple_lock_data(,event_table_lock)

const struct event_table_entry event_table_init[] =
{

  /*-----------------------------------------------------------------------*
   *  ETAP EVENT               TRACE STATUS       TEXT NAME        DYNAMIC *
   *-----------------------------------------------------------------------*/

#if	ETAP_EVENT_MONITOR
   {ETAP_P_USER_EVENT0      , ETAP_TRACE_OFF , "p_user_event0" 	  , STATIC},	
   {ETAP_P_USER_EVENT1      , ETAP_TRACE_OFF , "p_user_event1" 	  , STATIC},	
   {ETAP_P_USER_EVENT2      , ETAP_TRACE_OFF , "p_user_event2" 	  , STATIC},	
   {ETAP_P_USER_EVENT3      , ETAP_TRACE_OFF , "p_user_event3" 	  , STATIC},	
   {ETAP_P_USER_EVENT4      , ETAP_TRACE_OFF , "p_user_event4" 	  , STATIC},	
   {ETAP_P_USER_EVENT5      , ETAP_TRACE_OFF , "p_user_event5" 	  , STATIC},	
   {ETAP_P_USER_EVENT6      , ETAP_TRACE_OFF , "p_user_event6" 	  , STATIC},	
   {ETAP_P_USER_EVENT7      , ETAP_TRACE_OFF , "p_user_event7" 	  , STATIC},	
   {ETAP_P_USER_EVENT8      , ETAP_TRACE_OFF , "p_user_event8" 	  , STATIC},	
   {ETAP_P_USER_EVENT9      , ETAP_TRACE_OFF , "p_user_event9" 	  , STATIC},	
   {ETAP_P_USER_EVENT10     , ETAP_TRACE_OFF , "p_user_event10"	  , STATIC},	
   {ETAP_P_USER_EVENT11     , ETAP_TRACE_OFF , "p_user_event11"	  , STATIC},	
   {ETAP_P_USER_EVENT12     , ETAP_TRACE_OFF , "p_user_event12"	  , STATIC},	
   {ETAP_P_USER_EVENT13     , ETAP_TRACE_OFF , "p_user_event13"	  , STATIC},	
   {ETAP_P_USER_EVENT14     , ETAP_TRACE_OFF , "p_user_event14"	  , STATIC},	
   {ETAP_P_USER_EVENT15     , ETAP_TRACE_OFF , "p_user_event15"	  , STATIC},	
   {ETAP_P_USER_EVENT16     , ETAP_TRACE_OFF , "p_user_event16"	  , STATIC},	
   {ETAP_P_USER_EVENT17     , ETAP_TRACE_OFF , "p_user_event17"	  , STATIC},	
   {ETAP_P_USER_EVENT18     , ETAP_TRACE_OFF , "p_user_event18"	  , STATIC},	
   {ETAP_P_USER_EVENT19     , ETAP_TRACE_OFF , "p_user_event19"	  , STATIC},	
   {ETAP_P_USER_EVENT20     , ETAP_TRACE_OFF , "p_user_event20"	  , STATIC},	
   {ETAP_P_USER_EVENT21     , ETAP_TRACE_OFF , "p_user_event21"	  , STATIC},	
   {ETAP_P_USER_EVENT22     , ETAP_TRACE_OFF , "p_user_event22"	  , STATIC},	
   {ETAP_P_USER_EVENT23     , ETAP_TRACE_OFF , "p_user_event23"	  , STATIC},	
   {ETAP_P_USER_EVENT24     , ETAP_TRACE_OFF , "p_user_event24"	  , STATIC},	
   {ETAP_P_USER_EVENT25     , ETAP_TRACE_OFF , "p_user_event25"	  , STATIC},	
   {ETAP_P_USER_EVENT26     , ETAP_TRACE_OFF , "p_user_event26"	  , STATIC},	
   {ETAP_P_USER_EVENT27     , ETAP_TRACE_OFF , "p_user_event27"	  , STATIC},	
   {ETAP_P_USER_EVENT28     , ETAP_TRACE_OFF , "p_user_event28"	  , STATIC},	
   {ETAP_P_USER_EVENT29     , ETAP_TRACE_OFF , "p_user_event29"	  , STATIC},	
   {ETAP_P_USER_EVENT30     , ETAP_TRACE_OFF , "p_user_event30"	  , STATIC},	
   {ETAP_P_USER_EVENT31     , ETAP_TRACE_OFF , "p_user_event31"	  , STATIC},	
   {ETAP_P_SYSCALL_MACH     , ETAP_TRACE_OFF , "p_syscall_mach"   , STATIC},	
   {ETAP_P_SYSCALL_UNIX     , ETAP_TRACE_OFF , "p_syscall_unix"   , STATIC},	
   {ETAP_P_THREAD_LIFE      , ETAP_TRACE_OFF , "p_thread_life" 	  , STATIC},	
   {ETAP_P_THREAD_CTX	    , ETAP_TRACE_OFF , "p_thread_ctx" 	  , STATIC},	
   {ETAP_P_RPC		    , ETAP_TRACE_OFF , "p_rpc"		  , STATIC},
   {ETAP_P_INTERRUPT        , ETAP_TRACE_OFF , "p_interrupt"      , STATIC},
   {ETAP_P_ACT_ABORT        , ETAP_TRACE_OFF , "p_act_abort"      , STATIC},	
   {ETAP_P_PRIORITY         , ETAP_TRACE_OFF , "p_priority"       , STATIC},	
   {ETAP_P_EXCEPTION        , ETAP_TRACE_OFF , "p_exception"      , STATIC},	
   {ETAP_P_DEPRESSION       , ETAP_TRACE_OFF , "p_depression"     , STATIC},	
   {ETAP_P_MISC		    , ETAP_TRACE_OFF , "p_misc"		  , STATIC},		
   {ETAP_P_DETAP	    , ETAP_TRACE_OFF , "p_detap"	  , STATIC}, 
#endif	/* ETAP_EVENT_MONITOR */

#if	ETAP_LOCK_TRACE
   {ETAP_VM_BUCKET          , ETAP_TRACE_OFF , "vm_bucket"        , STATIC},/**/
   {ETAP_VM_HIMEM           , ETAP_TRACE_OFF , "vm_himem"         , STATIC},
   {ETAP_VM_MAP             , ETAP_TRACE_OFF , "vm_map"           ,      1},
   {ETAP_VM_MAP_I           , ETAP_TRACE_OFF , "vm_map_i"         ,      2},
   {ETAP_VM_MEMMAN          , ETAP_TRACE_OFF , "vm_memman"        , STATIC},/**/
   {ETAP_VM_MSYNC           , ETAP_TRACE_OFF , "vm_msync"         ,      3},
   {ETAP_VM_OBJ             , ETAP_TRACE_OFF , "vm_obj"           ,      4},
   {ETAP_VM_OBJ_CACHE       , ETAP_TRACE_OFF , "vm_obj_cache"     ,      5},
   {ETAP_VM_PAGE_ALLOC      , ETAP_TRACE_OFF , "vm_page_alloc"    , STATIC},/**/
   {ETAP_VM_PAGEOUT         , ETAP_TRACE_OFF , "vm_pageout"       , STATIC},
   {ETAP_VM_PAGEQ           , ETAP_TRACE_OFF , "vm_pageq"         , STATIC},
   {ETAP_VM_PAGEQ_FREE      , ETAP_TRACE_OFF , "vm_pageq_free"    , STATIC},
   {ETAP_VM_PMAP            , ETAP_TRACE_OFF , "vm_pmap"          ,      6},
   {ETAP_VM_PMAP_CACHE      , ETAP_TRACE_OFF , "vm_pmap_cache"    , STATIC},
   {ETAP_VM_PMAP_FREE       , ETAP_TRACE_OFF , "vm_pmap_free"     , STATIC},
   {ETAP_VM_PMAP_KERNEL     , ETAP_TRACE_OFF , "vm_pmap_kern"     , STATIC},
   {ETAP_VM_PMAP_SYS        , ETAP_TRACE_OFF , "vm_pmap_sys"      ,      7},
   {ETAP_VM_PMAP_SYS_I      , ETAP_TRACE_OFF , "vm_pmap_sys_i"    ,      8},
   {ETAP_VM_PMAP_UPDATE     , ETAP_TRACE_OFF , "vm_pmap_update"   , STATIC},
   {ETAP_VM_PREPPIN	    , ETAP_TRACE_OFF , "vm_preppin"       , STATIC},
   {ETAP_VM_RESULT          , ETAP_TRACE_OFF , "vm_result"        ,      9},
   {ETAP_VM_TEST            , ETAP_TRACE_OFF , "vm_tes"           , STATIC},/**/
   {ETAP_VM_PMAP_PHYSENTRIES, ETAP_TRACE_OFF , "vm_pmap_physentries", STATIC},
   {ETAP_VM_PMAP_SID        , ETAP_TRACE_OFF , "vm_pmap_sid"      , STATIC},
   {ETAP_VM_PMAP_PTE        , ETAP_TRACE_OFF , "vm_pmap_pte"      , STATIC},
   {ETAP_VM_PMAP_PTE_OVFLW  , ETAP_TRACE_OFF , "vm_pmap_pte_ovflw", STATIC},
   {ETAP_VM_PMAP_TLB        , ETAP_TRACE_OFF , "vm_pmap_tlb"      , STATIC},

   {ETAP_IPC_IHGB	    , ETAP_TRACE_OFF , "ipc_ihgb"         ,     10},/**/
   {ETAP_IPC_IS 	    , ETAP_TRACE_OFF , "ipc_is"           ,     11},/**/
   {ETAP_IPC_IS_REF	    , ETAP_TRACE_OFF , "ipc_is_ref"       ,     12},/**/
   {ETAP_IPC_MQUEUE	    , ETAP_TRACE_OFF , "ipc_mqueue"       , STATIC},/**/
   {ETAP_IPC_OBJECT	    , ETAP_TRACE_OFF , "ipc_object"       , STATIC},/**/
   {ETAP_IPC_PORT_MULT	    , ETAP_TRACE_OFF , "ipc_port_mult"    ,     13},/**/
   {ETAP_IPC_PORT_TIME	    , ETAP_TRACE_OFF , "ipc_port_time"    ,     14},/**/
   {ETAP_IPC_RPC	    , ETAP_TRACE_OFF , "ipc_rpc"          ,     15},/**/
   {ETAP_IPC_PORT_ALLOCQ    , ETAP_TRACE_OFF , "ipc_port_allocq"  , STATIC},/**/

   {ETAP_IO_AHA             , ETAP_TRACE_OFF , "io_aha" 	  , STATIC},
   {ETAP_IO_CHIP            , ETAP_TRACE_OFF , "io_chip"          , STATIC},
   {ETAP_IO_DEV             , ETAP_TRACE_OFF , "io_dev"           ,     16},/**/
   {ETAP_IO_DEV_NUM         , ETAP_TRACE_OFF , "io_dev_num"       , STATIC},
   {ETAP_IO_DEV_PAGEH       , ETAP_TRACE_OFF , "io_dev_pageh"     , STATIC},/**/
   {ETAP_IO_DEV_PAGER       , ETAP_TRACE_OFF , "io_dev_pager"     , STATIC},/**/
   {ETAP_IO_DEV_PORT        , ETAP_TRACE_OFF , "io_dev_port"      , STATIC},/**/
   {ETAP_IO_DEV_REF         , ETAP_TRACE_OFF , "io_dev_new"       ,     17},/**/
   {ETAP_IO_DEVINS          , ETAP_TRACE_OFF , "io_devins"        , STATIC},
   {ETAP_IO_DONE_LIST       , ETAP_TRACE_OFF , "io_done_list"     , STATIC},
   {ETAP_IO_DONE_Q          , ETAP_TRACE_OFF , "io_doneq"         ,     18},
   {ETAP_IO_DONE_REF        , ETAP_TRACE_OFF , "io_done_ref"      ,     19},
   {ETAP_IO_EAHA            , ETAP_TRACE_OFF , "io_eaha" 	  , STATIC},
   {ETAP_IO_HD_PROBE        , ETAP_TRACE_OFF , "io_hd_probe"      , STATIC},
   {ETAP_IO_IOPB            , ETAP_TRACE_OFF , "io_iopb"          , STATIC},
   {ETAP_IO_KDQ	            , ETAP_TRACE_OFF , "io_kdq"           , STATIC},
   {ETAP_IO_KDTTY           , ETAP_TRACE_OFF , "io_kdtty"         , STATIC},
   {ETAP_IO_REQ             , ETAP_TRACE_OFF , "io_req"           ,     20},
   {ETAP_IO_TARGET          , ETAP_TRACE_OFF , "io_target"        , STATIC},
   {ETAP_IO_TTY             , ETAP_TRACE_OFF , "io_tty"           , STATIC},
   {ETAP_IO_IOP_LOCK        , ETAP_TRACE_OFF , "io_iop"           , STATIC},/**/
   {ETAP_IO_DEV_NAME        , ETAP_TRACE_OFF , "io_dev_name"      , STATIC},/**/
   {ETAP_IO_CDLI            , ETAP_TRACE_OFF , "io_cdli"          , STATIC},/**/
   {ETAP_IO_HIPPI_FILTER    , ETAP_TRACE_OFF , "io_hippi_filter"  , STATIC},/**/
   {ETAP_IO_HIPPI_SRC       , ETAP_TRACE_OFF , "io_hippi_src"     , STATIC},/**/
   {ETAP_IO_HIPPI_DST       , ETAP_TRACE_OFF , "io_hippi_dst"     , STATIC},/**/
   {ETAP_IO_HIPPI_PKT       , ETAP_TRACE_OFF , "io_hippi_pkt"     , STATIC},/**/
   {ETAP_IO_NOTIFY          , ETAP_TRACE_OFF , "io_notify"        , STATIC},/**/
   {ETAP_IO_DATADEV         , ETAP_TRACE_OFF , "io_data_device"   , STATIC},/**/
   {ETAP_IO_OPEN	    , ETAP_TRACE_OFF , "io_open"	  , STATIC},
   {ETAP_IO_OPEN_I	    , ETAP_TRACE_OFF , "io_open_i"	  , STATIC},

   {ETAP_THREAD_ACT         , ETAP_TRACE_OFF , "th_act"           ,     21},
   {ETAP_THREAD_ACTION      , ETAP_TRACE_OFF , "th_action"        , STATIC},
   {ETAP_THREAD_LOCK        , ETAP_TRACE_OFF , "th_lock"          ,     22},
   {ETAP_THREAD_LOCK_SET    , ETAP_TRACE_OFF , "th_lock_set"      ,     23},
   {ETAP_THREAD_NEW         , ETAP_TRACE_OFF , "th_new"           ,     24},
   {ETAP_THREAD_PSET        , ETAP_TRACE_OFF , "th_pset"          , STATIC},/**/
   {ETAP_THREAD_PSET_ALL    , ETAP_TRACE_OFF , "th_pset_all"      , STATIC},
   {ETAP_THREAD_PSET_RUNQ   , ETAP_TRACE_OFF , "th_pset_runq"     , STATIC},
   {ETAP_THREAD_PSET_IDLE   , ETAP_TRACE_OFF , "th_pset_idle"     , STATIC},
   {ETAP_THREAD_PSET_QUANT  , ETAP_TRACE_OFF , "th_pset_quant"    , STATIC},
   {ETAP_THREAD_PROC        , ETAP_TRACE_OFF , "th_proc"          , STATIC},
   {ETAP_THREAD_PROC_RUNQ   , ETAP_TRACE_OFF , "th_proc_runq"     , STATIC},
   {ETAP_THREAD_REAPER      , ETAP_TRACE_OFF , "th_reaper"        , STATIC},
   {ETAP_THREAD_RPC         , ETAP_TRACE_OFF , "th_rpc"           ,     25},
   {ETAP_THREAD_SEMA        , ETAP_TRACE_OFF , "th_sema"          ,     26},
   {ETAP_THREAD_STACK       , ETAP_TRACE_OFF , "th_stack"         , STATIC},
   {ETAP_THREAD_STACK_USAGE , ETAP_TRACE_OFF , "th_stack_usage"   , STATIC},
   {ETAP_THREAD_TASK_NEW    , ETAP_TRACE_OFF , "th_task_new"      ,     27},
   {ETAP_THREAD_TASK_ITK    , ETAP_TRACE_OFF , "th_task_itk"      ,     28},
   {ETAP_THREAD_ULOCK       , ETAP_TRACE_OFF , "th_ulock"         ,     29},
   {ETAP_THREAD_WAIT        , ETAP_TRACE_OFF , "th_wait"          , STATIC},
   {ETAP_THREAD_WAKE        , ETAP_TRACE_OFF , "th_wake"          ,     30},
   {ETAP_THREAD_ACT_LIST    , ETAP_TRACE_OFF , "th_act_list"      ,     31},
   {ETAP_THREAD_TASK_SWAP   , ETAP_TRACE_OFF , "th_task_swap"     ,     32},
   {ETAP_THREAD_TASK_SWAPOUT, ETAP_TRACE_OFF , "th_task_swapout"  ,     33},
   {ETAP_THREAD_SWAPPER     , ETAP_TRACE_OFF , "th_swapper"       , STATIC},
 
   {ETAP_NET_IFQ            , ETAP_TRACE_OFF , "net_ifq"          , STATIC},
   {ETAP_NET_KMSG           , ETAP_TRACE_OFF , "net_kmsg"         , STATIC},
   {ETAP_NET_MBUF           , ETAP_TRACE_OFF , "net_mbuf"         , STATIC},/**/
   {ETAP_NET_POOL           , ETAP_TRACE_OFF , "net_pool"         , STATIC},
   {ETAP_NET_Q              , ETAP_TRACE_OFF , "net_q"            , STATIC},
   {ETAP_NET_QFREE          , ETAP_TRACE_OFF , "net_qfree"        , STATIC},
   {ETAP_NET_RCV            , ETAP_TRACE_OFF , "net_rcv"          , STATIC},
   {ETAP_NET_RCV_PLIST      , ETAP_TRACE_OFF , "net_rcv_plist"    , STATIC},/**/
   {ETAP_NET_THREAD         , ETAP_TRACE_OFF , "net_thread"       , STATIC},

   {ETAP_NORMA_XMM          , ETAP_TRACE_OFF , "norma_xmm"        , STATIC},
   {ETAP_NORMA_XMMOBJ       , ETAP_TRACE_OFF , "norma_xmmobj"     , STATIC},
   {ETAP_NORMA_XMMCACHE     , ETAP_TRACE_OFF , "norma_xmmcache"	  , STATIC},
   {ETAP_NORMA_MP           , ETAP_TRACE_OFF , "norma_mp"	  , STATIC},
   {ETAP_NORMA_VOR          , ETAP_TRACE_OFF , "norma_vor"	  , STATIC},/**/
   {ETAP_NORMA_TASK         , ETAP_TRACE_OFF , "norma_task"	  , 38},/**/

   {ETAP_DIPC_CLEANUP	    , ETAP_TRACE_OFF , "dipc_cleanup"     , STATIC},/**/
   {ETAP_DIPC_MSG_PROG	    , ETAP_TRACE_OFF , "dipc_msgp_prog"   , STATIC},/**/
   {ETAP_DIPC_PREP_QUEUE    , ETAP_TRACE_OFF , "dipc_prep_queue"  , STATIC},/**/
   {ETAP_DIPC_PREP_FILL	    , ETAP_TRACE_OFF , "dipc_prep_fill"   , STATIC},/**/
   {ETAP_DIPC_MIGRATE	    , ETAP_TRACE_OFF , "dipc_migrate"	  , STATIC},/**/
   {ETAP_DIPC_DELIVER	    , ETAP_TRACE_OFF , "dipc_deliver"	  , STATIC},/**/
   {ETAP_DIPC_RECV_SYNC	    , ETAP_TRACE_OFF , "dipc_recv_sync"	  , STATIC},/**/
   {ETAP_DIPC_RPC	    , ETAP_TRACE_OFF , "dipc_rpc"	  , STATIC},/**/
   {ETAP_DIPC_MSG_REQ	    , ETAP_TRACE_OFF , "dipc_msg_req"	  , STATIC},/**/
   {ETAP_DIPC_MSG_ORDER	    , ETAP_TRACE_OFF , "dipc_msg_order"	  , STATIC},/**/
   {ETAP_DIPC_MSG_PREPQ	    , ETAP_TRACE_OFF , "dipc_msg_prepq"	  , STATIC},/**/
   {ETAP_DIPC_MSG_FREE	    , ETAP_TRACE_OFF , "dipc_msg_free"	  , STATIC},/**/
   {ETAP_DIPC_KMSG_AST	    , ETAP_TRACE_OFF , "dipc_kmsg_ast"	  , STATIC},/**/
   {ETAP_DIPC_TEST_LOCK	    , ETAP_TRACE_OFF , "dipc_test_lock"	  , STATIC},/**/
   {ETAP_DIPC_SPINLOCK	    , ETAP_TRACE_OFF , "dipc_spinlock"	  , STATIC},/**/
   {ETAP_DIPC_TRACE	    , ETAP_TRACE_OFF , "dipc_trace"	  , STATIC},/**/
   {ETAP_DIPC_REQ_CALLBACK  , ETAP_TRACE_OFF , "dipc_req_clbck"	  , STATIC},/**/
   {ETAP_DIPC_PORT_NAME	    , ETAP_TRACE_OFF , "dipc_port_name"	  , STATIC},/**/
   {ETAP_DIPC_RESTART_PORT  , ETAP_TRACE_OFF , "dipc_restart_port", STATIC},/**/
   {ETAP_DIPC_ZERO_PAGE	    , ETAP_TRACE_OFF , "dipc_zero_page"	  , STATIC},/**/
   {ETAP_DIPC_BLOCKED_NODE  , ETAP_TRACE_OFF , "dipc_blocked_node", STATIC},/**/
   {ETAP_DIPC_TIMER         , ETAP_TRACE_OFF , "dipc_timer"       , STATIC},/**/
   {ETAP_DIPC_SPECIAL_PORT  , ETAP_TRACE_OFF , "dipc_special_port", STATIC},/**/

   {ETAP_KKT_TEST_WORK	    , ETAP_TRACE_OFF , "kkt_test_work"	  , STATIC},/**/
   {ETAP_KKT_TEST_MP	    , ETAP_TRACE_OFF , "kkt_work_mp"	  , STATIC},/**/
   {ETAP_KKT_NODE	    , ETAP_TRACE_OFF , "kkt_node"	  , STATIC},/**/
   {ETAP_KKT_CHANNEL_LIST   , ETAP_TRACE_OFF , "kkt_channel_list" , STATIC},/**/
   {ETAP_KKT_CHANNEL        , ETAP_TRACE_OFF , "kkt_channel"      , STATIC},/**/
   {ETAP_KKT_HANDLE         , ETAP_TRACE_OFF , "kkt_handle"       , STATIC},/**/
   {ETAP_KKT_MAP	    , ETAP_TRACE_OFF , "kkt_map"	  , STATIC},/**/
   {ETAP_KKT_RESOURCE	    , ETAP_TRACE_OFF , "kkt_resource"     , STATIC},/**/

   {ETAP_XKERNEL_MASTER	    , ETAP_TRACE_OFF , "xkernel_master"   , STATIC},/**/
   {ETAP_XKERNEL_EVENT	    , ETAP_TRACE_OFF , "xkernel_event"    , STATIC},/**/
   {ETAP_XKERNEL_ETHINPUT   , ETAP_TRACE_OFF , "xkernel_input"    , STATIC},/**/

   {ETAP_MISC_AST	    , ETAP_TRACE_OFF , "m_ast"            , STATIC},
   {ETAP_MISC_CLOCK	    , ETAP_TRACE_OFF , "m_clock"          , STATIC},
   {ETAP_MISC_EMULATE	    , ETAP_TRACE_OFF , "m_emulate"        ,     34},
   {ETAP_MISC_EVENT         , ETAP_TRACE_OFF , "m_event"          , STATIC},
   {ETAP_MISC_KDB	    , ETAP_TRACE_OFF , "m_kdb"            , STATIC},
   {ETAP_MISC_PCB	    , ETAP_TRACE_OFF , "m_pcb"            ,     35},
   {ETAP_MISC_PRINTF	    , ETAP_TRACE_OFF , "m_printf"         , STATIC},
   {ETAP_MISC_Q             , ETAP_TRACE_OFF , "m_q"              , STATIC},
   {ETAP_MISC_RPC_SUBSYS    , ETAP_TRACE_OFF , "m_rpc_sub"        ,     36},
   {ETAP_MISC_RT_CLOCK      , ETAP_TRACE_OFF , "m_rt_clock"       , STATIC},
   {ETAP_MISC_SD_POOL       , ETAP_TRACE_OFF , "m_sd_pool"        , STATIC},
   {ETAP_MISC_TIMER         , ETAP_TRACE_OFF , "m_timer"          , STATIC},
   {ETAP_MISC_UTIME	    , ETAP_TRACE_OFF , "m_utime"          , STATIC},
   {ETAP_MISC_XPR           , ETAP_TRACE_OFF , "m_xpr"            , STATIC},
   {ETAP_MISC_ZONE          , ETAP_TRACE_OFF , "m_zone"           ,     37},
   {ETAP_MISC_ZONE_ALL      , ETAP_TRACE_OFF , "m_zone_all"       , STATIC},
   {ETAP_MISC_ZONE_GET      , ETAP_TRACE_OFF , "m_zone_get"       , STATIC},
   {ETAP_MISC_ZONE_PTABLE   , ETAP_TRACE_OFF , "m_zone_ptable"    , STATIC},/**/
   {ETAP_MISC_LEDGER        , ETAP_TRACE_OFF , "m_ledger"         , STATIC},/**/
   {ETAP_MISC_SCSIT_TGT     , ETAP_TRACE_OFF , "m_scsit_tgt_lock" , STATIC},/**/
   {ETAP_MISC_SCSIT_SELF    , ETAP_TRACE_OFF , "m_scsit_self_lock", STATIC},/**/
   {ETAP_MISC_SPL	    , ETAP_TRACE_OFF , "m_spl_lock"	  , STATIC},/**/
   {ETAP_MISC_MASTER	    , ETAP_TRACE_OFF , "m_master"	  , STATIC},/**/
   {ETAP_MISC_FLOAT         , ETAP_TRACE_OFF , "m_float"          , STATIC},/**/
   {ETAP_MISC_GROUP         , ETAP_TRACE_OFF , "m_group"          , STATIC},/**/
   {ETAP_MISC_FLIPC         , ETAP_TRACE_OFF , "m_flipc"          , STATIC},/**/
   {ETAP_MISC_MP_IO         , ETAP_TRACE_OFF , "m_mp_io"          , STATIC},/**/
   {ETAP_MISC_KERNEL_TEST   , ETAP_TRACE_OFF , "m_kernel_test"    , STATIC},/**/
 
   {ETAP_NO_TRACE           , ETAP_TRACE_OFF , "NEVER_TRACE"      , STATIC},
#endif	/* ETAP_LOCK_TRACE */
};

/*
 * Variable initially pointing to the event table, then to its mappable
 * copy.  The cast is needed to discard the `const' qualifier; without it
 * gcc issues a warning.
 */
event_table_t event_table = (event_table_t) event_table_init;

/*
 * Linked list of pointers into event_table_init[] so they can be switched
 * into the mappable copy when it is made.
 */
struct event_table_chain *event_table_chain;

/*
 *  max number of event types in the event table
 */

int event_table_max = sizeof(event_table_init)/sizeof(struct event_table_entry);

const struct subs_table_entry subs_table_init[] =
{
  /*------------------------------------------*
   *  ETAP SUBSYSTEM           TEXT NAME      *
   *------------------------------------------*/

#if	ETAP_EVENT_MONITOR
   {ETAP_SUBS_PROBE        ,  "event_probes" },
#endif	/* ETAP_EVENT_MONITOR */

#if	ETAP_LOCK_TRACE
   {ETAP_SUBS_LOCK_DIPC	   ,  "lock_dipc"    },
   {ETAP_SUBS_LOCK_IO      ,  "lock_io"      },
   {ETAP_SUBS_LOCK_IPC     ,  "lock_ipc"     },
   {ETAP_SUBS_LOCK_KKT     ,  "lock_kkt"     },
   {ETAP_SUBS_LOCK_MISC    ,  "lock_misc"    },
   {ETAP_SUBS_LOCK_NET     ,  "lock_net"     },
   {ETAP_SUBS_LOCK_NORMA   ,  "lock_norma"   },
   {ETAP_SUBS_LOCK_THREAD  ,  "lock_thread"  },
   {ETAP_SUBS_LOCK_VM      ,  "lock_vm"      },
   {ETAP_SUBS_LOCK_XKERNEL ,  "lock_xkernel" },
#endif	/* ETAP_LOCK_TRACE */
};

/*
 * Variable initially pointing to the subsystem table, then to its mappable
 * copy.
 */
subs_table_t subs_table = (subs_table_t) subs_table_init;

/*
 *  max number of subsystem types in the subsystem table
 */

int subs_table_max = sizeof(subs_table_init)/sizeof(struct subs_table_entry);

#if ETAP_MONITOR
#define MAX_NAME_SIZE   35

#define SYS_TABLE_MACH_TRAP     0
#define SYS_TABLE_MACH_MESSAGE  1
#define SYS_TABLE_UNIX_SYSCALL  2
#define SYS_TABLE_INTERRUPT     3
#define SYS_TABLE_EXCEPTION     4


extern char *system_table_lookup (unsigned int table,
                                  unsigned int number);


char *mach_trap_names[] = {
/*   0 */       "undefined",
/*   1 */       NULL,
/*   2 */       NULL,
/*   3 */       NULL,
/*   4 */       NULL,
/*   5 */       NULL,
/*   6 */       NULL,
/*   7 */       NULL,
/*   8 */       NULL,
/*   9 */       NULL,
/*  10 */       NULL,
/*  11 */       NULL,
/*  12 */       NULL,
/*  13 */       NULL,
/*  14 */       NULL,
/*  15 */       NULL,
/*  16 */       NULL,
/*  17 */       NULL,
/*  18 */       NULL,
/*  19 */       NULL,
/*  20 */       NULL,
/*  21 */       NULL,
/*  22 */       NULL,
/*  23 */       NULL,
/*  24 */       NULL,
/*  25 */       NULL,
/*  26 */       "mach_reply_port",
/*  27 */       "mach_thread_self",
/*  28 */       "mach_task_self",
/*  29 */       "mach_host_self",
/*  30 */       "vm_read_overwrite",
/*  31 */       "vm_write",
/*  32 */       "mach_msg_overwrite_trap",
/*  33 */       NULL,
/*  34 */       NULL,
#ifdef i386
/*  35 */       "mach_rpc_trap",
/*  36 */       "mach_rpc_return_trap",
#else
/*  35 */	NULL,
/*  36 */	NULL,
#endif /* i386 */
/*  37 */       NULL,
/*  38 */       NULL,
/*  39 */       NULL,
/*  40 */       NULL,
/*  41 */       "init_process",
/*  42 */       NULL,
/*  43 */       "map_fd",
/*  44 */       NULL,
/*  45 */       NULL,
/*  46 */       NULL,
/*  47 */       NULL,
/*  48 */       NULL,
/*  49 */       NULL,
/*  50 */       NULL,
/*  51 */       NULL,
/*  52 */       NULL,
/*  53 */       NULL,
/*  54 */       NULL,
/*  55 */       NULL,
/*  56 */       NULL,
/*  57 */       NULL,
/*  58 */       NULL,
/*  59 */       "swtch_pri",
/*  60 */       "swtch",
/*  61 */       "thread_switch",
/*  62 */       "clock_sleep_trap",
/*  63 */       NULL,
/*  64 */       NULL,
/*  65 */       NULL,
/*  66 */       NULL,
/*  67 */       NULL,
/*  68 */       NULL,
/*  69 */       NULL,
/*  70 */       NULL,
/*  71 */       NULL,
/*  72 */       NULL,
/*  73 */       NULL,
/*  74 */       NULL,
/*  75 */       NULL,
/*  76 */       NULL,
/*  77 */       NULL,
/*  78 */       NULL,
/*  79 */       NULL,
/*  80 */       NULL,
/*  81 */       NULL,
/*  82 */       NULL,
/*  83 */       NULL,
/*  84 */       NULL,
/*  85 */       NULL,
/*  86 */       NULL,
/*  87 */       NULL,
/*  88 */       NULL,
/*  89 */       NULL,
/*  90 */       NULL,
/*  91 */       NULL,
/*  92 */       NULL,
/*  93 */       NULL,
/*  94 */       NULL,
/*  95 */       NULL,
/*  96 */       NULL,
/*  97 */       NULL,
/*  98 */       NULL,
/*  99 */       NULL,
/* 100 */       NULL,
/* 101 */       NULL,
/* 102 */       NULL,
/* 103 */       NULL,
/* 104 */       NULL,
/* 105 */       NULL,
/* 106 */       NULL,
/* 107 */       NULL,
/* 108 */       NULL,
/* 109 */       NULL,
};
#define N_MACH_TRAP_NAMES (sizeof mach_trap_names / sizeof mach_trap_names[0])
#define mach_trap_name(nu) \
        (((nu) < N_MACH_TRAP_NAMES) ? mach_trap_names[nu] : NULL)

struct table_entry {
        char    name[MAX_NAME_SIZE];
        u_int   number;
};

/*
 * Mach message table
 *
 * Note: Most mach system calls are actually implemented as messages.
 */
struct table_entry mach_message_table[] = {
        subsystem_to_name_map_bootstrap,
        subsystem_to_name_map_clock,
        subsystem_to_name_map_clock_reply,
        subsystem_to_name_map_default_pager_object,
        subsystem_to_name_map_device,
        subsystem_to_name_map_device_reply,
        subsystem_to_name_map_device_request,
        subsystem_to_name_map_exc,
/*        subsystem_to_name_map_mach,*/
        subsystem_to_name_map_mach_debug,
/*        subsystem_to_name_map_mach_host,*/
        subsystem_to_name_map_mach_norma,
        subsystem_to_name_map_mach_port,
        subsystem_to_name_map_memory_object,
        subsystem_to_name_map_memory_object_default,
        subsystem_to_name_map_notify,
        subsystem_to_name_map_prof,
        subsystem_to_name_map_sync
};

int     mach_message_table_entries = sizeof(mach_message_table) /
                                     sizeof(struct table_entry);


#endif

/*
 *  ================================
 *  Initialization routines for ETAP
 *  ================================
 */

/*
 *  ROUTINE:    etap_init_phase1		[internal]
 *
 *  FUNCTION:   Event trace instrumentation initialization phase
 *              one of two.  The static phase.  The cumulative buffer
 *              is initialized.
 *
 *  NOTES:      The cumulative buffer is statically allocated and
 *              must be initialized before the first simple_lock_init()
 *              or lock_init() call is made.
 *
 *              The first lock init call is made before dynamic allocation
 *              is available.  Hence, phase one is executed before dynamic
 *              memory allocation is available.
 *
 */

void
etap_init_phase1(void)
{
#if	ETAP_LOCK_ACCUMULATE || MACH_ASSERT
	int x;
#if	MACH_ASSERT
	boolean_t out_of_order;
#endif	/* MACH_ASSERT */
#endif	/* ETAP_LOCK_ACCUMULATE || MACH_ASSERT */

#if	ETAP_LOCK_ACCUMULATE
        /*
         *  Initialize Cumulative Buffer
         *
         *  Note: The cumulative buffer is statically allocated.
         *        This static allocation is necessary since most
         *        of the lock_init calls are made before dynamic
         *        allocation routines are available.
         */

        /*
         *  Align cumulative buffer pointer to a page boundary
	 *  (so it can be maped).
         */

        bzero(&cbuff_allocated[0], CBUFF_ALLOCATED_SIZE);
        cbuff = (cumulative_buffer_t) round_page(&cbuff_allocated);

	simple_lock_init(&cbuff_lock, ETAP_NO_TRACE);

        /*
         *  Set the starting point for cumulative buffer entry
	 *  reservations.
         *
         *  This value must leave enough head room in the
         *  cumulative buffer to contain all dynamic events.
         */

        for (x=0; x < event_table_max; x++)
		if (event_table[x].dynamic > cbuff->static_start)
			cbuff->static_start = event_table[x].dynamic;

        cbuff->next = cbuff->static_start;
#endif	/* ETAP_LOCK_ACCUMULATE */

	/*
	 * Initialize the event table lock
	 */

	simple_lock_init(&event_table_lock, ETAP_NO_TRACE);

#if	MACH_ASSERT
	/*
	 * Check that events are in numerical order so we can do a binary
	 * search on them.  Even better would be to make event numbers be
	 * simple contiguous indexes into event_table[], but that would
	 * break the coding of subsystems in the event number.
	 */
	out_of_order = FALSE;
	for (x = 1; x < event_table_max; x++) {
		if (event_table[x - 1].event > event_table[x].event) {
			printf("events out of order: %s > %s\n",
			       event_table[x - 1].name, event_table[x].name);
			out_of_order = TRUE;
		}
	}
	if (out_of_order)
		panic("etap_init_phase1");
#endif	/* MACH_ASSERT */
}


/*
 *  ROUTINE:    etap_init_phase2		[internal]
 *
 *  FUNCTION:   Event trace instrumentation initialization phase
 *              two of two.  The dynamic phase.  The monitored buffers
 *              are dynamically allocated and initialized.  Cumulative
 *              dynamic entry locks are allocated and initialized.  The
 *              start_data_pool is initialized.
 *
 *  NOTES:      Phase two is executed once dynamic memory allocation
 *              is available.
 *
 */

void
etap_init_phase2(void)
{
        int size;
        int x;
        int ret;
	vm_offset_t table_copy;
	struct event_table_chain *chainp;

	/*
	 * Make mappable copies of the event_table and the subs_table.
	 * These tables were originally mapped as they appear in the
	 * kernel image, but that meant that other kernel variables could
	 * end up being mapped with them, which is ugly.  It also didn't
	 * work on the HP/PA, where pages with physical address == virtual
	 * do not have real pmap entries allocated and therefore can't be
	 * mapped elsewhere.
	 */
	size = sizeof event_table_init + sizeof subs_table_init;
	ret = kmem_alloc(kernel_map, &table_copy, size);
	if (ret != KERN_SUCCESS)
		panic("ETAP: error allocating table copies");
	event_table = (event_table_t) table_copy;
	subs_table = (subs_table_t) (table_copy + sizeof event_table_init);
	bcopy((char *) event_table_init, (char *) event_table,
	      sizeof event_table_init);
	bcopy((char *) subs_table_init, (char *) subs_table,
	      sizeof subs_table_init);

	/* Switch pointers from the old event_table to the new. */
	for (chainp = event_table_chain; chainp != NULL;
	     chainp = chainp->event_table_link) {
		x = chainp->event_tablep - event_table_init;
		assert(x < event_table_max);
		chainp->event_tablep = event_table + x;
	}

#if	ETAP_LOCK_ACCUMULATE

        /*
         *  Because several dynamic locks can point to a single
         *  cumulative buffer entry, dynamic lock writes to the
         *  entry are synchronized.
         *
         *  The spin locks are allocated here.
         *
         */
#if MACH_LDEBUG
	size = sizeof(simple_lock_t) * cbuff->static_start;
#else
	/*
         *  Note: These locks are different from traditional spin locks.
         *        They are of type int instead of type simple_lock_t.
         *        We can reduce lock size this way, since no tracing will
	 *	  EVER be performed on these locks.
         */
	size = sizeof(simple_lock_data_t) * cbuff->static_start;
#endif

        ret = kmem_alloc(kernel_map, (vm_offset_t *) &cbuff_locks, size);

        if (ret != KERN_SUCCESS)
		panic("ETAP: error allocating cumulative write locks");

#if MACH_LDEBUG
	for(x = 0; x < cbuff->static_start; ++x) {
		simple_lock_init(&cbuff_locks[x], ETAP_NO_TRACE);
	}
#else
        bzero((const char *) cbuff_locks, size);
#endif

#endif  /* ETAP_LOCK_ACCUMULATE */


#if	ETAP_MONITOR

        /*
         *  monitor buffer allocation
         */

        size = ((mbuff_entries-1) * sizeof(struct mbuff_entry)) +
		sizeof(struct monitor_buffer);

        for (x=0; x < NCPUS; x++) {
                ret = kmem_alloc(kernel_map,
				 (vm_offset_t *) &mbuff[x],
				 size);

                if (ret != KERN_SUCCESS)
			panic ("ETAP: error allocating monitor buffer\n");

                /* zero fill buffer */
                bzero((char *) mbuff[x], size);
        }

#endif  /* ETAP_MONITOR */


#if	ETAP_LOCK_TRACE

        /*
         *  Initialize the start_data_pool
         */

        init_start_data_pool();

#endif	/* ETAP_LOCK_TRACE */
}


#if	ETAP_LOCK_ACCUMULATE

/*
 *  ROUTINE:    etap_cbuff_reserve		[internal]
 *
 *  FUNCTION:   The cumulative buffer operation which returns a pointer
 *              to a free entry in the cumulative buffer.
 *
 *  NOTES:      Disables interrupts.
 *
 */

cbuff_entry_t
etap_cbuff_reserve(event_table_t etp)
{
	cbuff_entry_t  	avail;
	unsigned short	de;
	spl_t		s;

	/* see if type pointer is initialized */
	if (etp == EVENT_TABLE_NULL || etp->event == ETAP_NO_TRACE)
		return (CBUFF_ENTRY_NULL);

	/* check for DYNAMIC lock */
	if (de = etp->dynamic) {
		if (de <= cbuff->static_start)
			return (&cbuff->entry[de-1]);
		else {
			printf("ETAP: dynamic lock index error [%lu]\n", de);
			return (CBUFF_ENTRY_NULL);
		}
	}

	cumulative_buffer_lock(s);

	/* if buffer is full, reservation requests fail */
	if (cbuff->next >= ETAP_CBUFF_ENTRIES) {
		cumulative_buffer_unlock(s);
		return (CBUFF_ENTRY_NULL);
	}

	avail = &cbuff->entry[cbuff->next++];

	cumulative_buffer_unlock(s);

	return (avail);
}

#endif  /* ETAP_LOCK_ACCUMULATE */

/*
 *  ROUTINE:    etap_event_table_assign		[internal]
 *
 *  FUNCTION:   Returns a pointer to the assigned event type table entry,
 *              using the event type as the index key.
 *
 */

event_table_t
etap_event_table_find(etap_event_t event)
{
        int last_before, first_after, try;

	/* Binary search for the event number.  last_before is the highest-
	   numbered element known to be <= the number we're looking for;
	   first_after is the lowest-numbered element known to be >.  */
	last_before = 0;
	first_after = event_table_max;
	while (last_before < first_after) {
		try = (last_before + first_after) >> 1;
		if (event_table[try].event == event)
			return (&event_table[try]);
		else if (event_table[try].event < event)
			last_before = try;
		else
			first_after = try;
        }
	return EVENT_TABLE_NULL;
}

void
etap_event_table_assign(struct event_table_chain *chainp, etap_event_t event)
{
	event_table_t event_tablep;

	event_tablep = etap_event_table_find(event);
	if (event_tablep == EVENT_TABLE_NULL)
		printf("\nETAP: event not found in event table: %x\n", event);
	else {
		if (event_table == event_table_init) {
			chainp->event_table_link = event_table_chain;
			event_table_chain = chainp;
		}
		chainp->event_tablep = event_tablep;
	}
}

#endif	/* ETAP */

/*
 *
 *  MESSAGE:    etap_get_info			[exported]
 *
 *  FUNCTION:   provides the server with ETAP buffer configurations.
 *
 */

kern_return_t
etap_get_info(
	host_priv_t  host_priv,
	int          *et_entries,
	int          *st_entries,
	vm_offset_t  *et_offset,
	vm_offset_t  *st_offset,
	int          *cb_width,
	int          *mb_size,
	int          *mb_entries,
	int          *mb_cpus)
{

	if (host_priv == HOST_PRIV_NULL)
		return KERN_INVALID_ARGUMENT;

#if     ETAP
        *et_entries = event_table_max;
        *st_entries = subs_table_max;
        *et_offset  = (vm_offset_t) ((char*) event_table - 
				     trunc_page((char*) event_table));
        *st_offset  = (vm_offset_t) ((char*) subs_table - 
				     trunc_page((char*) subs_table));
#else   /* ETAP */
        *et_entries = 0;
        *st_entries = 0;
        *et_offset  = 0;
        *st_offset  = 0;
#endif  /* ETAP */

#if     ETAP_LOCK_ACCUMULATE
        *cb_width   = cbuff_width;
#else   /* ETAP_LOCK_ACCUMULATE */
        *cb_width   = 0;
#endif  /* ETAP_LOCK_ACCUMULATE */

#if     ETAP_MONITOR
        *mb_size     = ((mbuff_entries-1) * sizeof(struct mbuff_entry)) +
			sizeof(struct monitor_buffer);
        *mb_entries  = mbuff_entries;
        *mb_cpus     = NCPUS;
#else   /* ETAP_MONITOR */
        *mb_size     = 0;
        *mb_entries  = 0;
        *mb_cpus     = 0;
#endif  /* ETAP_MONITOR */

        return (KERN_SUCCESS);
}
    
/*
 *  ROUTINE:    etap_trace_event		[exported]
 *
 *  FUNCTION:   The etap_trace_event system call is the user's interface to
 *              the ETAP kernel instrumentation. 
 *
 *		This call allows the user to enable and disable tracing modes
 *              on specific event types.  The call also supports a reset option,
 *              where the cumulative buffer data and all event type tracing
 *              is reset to zero.  When the reset option is used, a new
 *              interval width can also be defined using the op parameter.
 *
 */

kern_return_t
etap_trace_event (
	unsigned short	mode,
	unsigned short	type,
	boolean_t	enable,
	unsigned int	nargs,
	unsigned short	args[])
{
#if     ETAP
        event_table_t   event_tablep;
	kern_return_t	ret;
        int             i, args_size;
        unsigned short  status_mask;
	unsigned short  *tmp_args;

        /*
         *  Initialize operation
         */

	if (mode == ETAP_RESET) {
		etap_trace_reset(nargs);
		return (KERN_SUCCESS);
	}

	status_mask = mode & type;

        /*
         *  Copy args array from user space to kernel space
         */
 
	args_size = nargs * sizeof *args;
        tmp_args = (unsigned short *) kalloc(args_size);

        if (tmp_args == NULL)
		return (KERN_NO_SPACE);

        if (copyin((const char *) args, (char *) tmp_args, args_size))
		return (KERN_INVALID_ADDRESS);

        /*
         *  Change appropriate status fields in the event table
         */

	event_table_lock();

	for (i = 0; i < nargs; i++) {
		if (tmp_args[i] != ETAP_NO_TRACE) {
			event_tablep = etap_event_table_find(tmp_args[i]);
			if (event_tablep == EVENT_TABLE_NULL)
				break;
			if (enable)
				event_tablep->status |= status_mask;
			else
				event_tablep->status &= ~status_mask;
		}
	}

	ret = (i < nargs) ? KERN_INVALID_ARGUMENT : KERN_SUCCESS;

	event_table_unlock();

	kfree((vm_offset_t) tmp_args, args_size);

        return (ret);

#else   /* ETAP */

        return (KERN_FAILURE);

#endif  /* ETAP */
}


#if     ETAP

/*
 *  ROUTINE:    etap_trace_reset		[internal]
 *
 *  FUNCTION:   Turns off all tracing and erases all the data accumulated
 *              in the cumulative buffer.  If the user defined a new 
 *		cumulative buffer interval width, it will be assigned here.
 *
 */
void
etap_trace_reset(int new_interval)
{
        event_table_t 	scan;
        int         	x;
        register    	s;

        /* 
         *  Wipe out trace fields in event table
         */

        scan = event_table;

	event_table_lock();

        for (x=0; x < event_table_max; x++) {
		scan->status = ETAP_TRACE_OFF;
		scan++;
        }

	event_table_unlock();

#if     ETAP_LOCK_ACCUMULATE

        /* 
         *  Wipe out cumulative buffer statistical fields for all entries
         */

	cumulative_buffer_lock(s);

        for (x=0; x < ETAP_CBUFF_ENTRIES; x++) {
		bzero ((char *) &cbuff->entry[x].hold, 
                            sizeof(struct cbuff_data));
		bzero ((char *) &cbuff->entry[x].wait,
                            sizeof(struct cbuff_data));
		bzero ((char *) &cbuff->entry[x].hold_interval[0],
                            sizeof(unsigned long) * ETAP_CBUFF_IBUCKETS);
		bzero ((char *) &cbuff->entry[x].wait_interval[0],
                            sizeof(unsigned long) * ETAP_CBUFF_IBUCKETS);
        }  

        /*
         *  Assign interval width if the user defined a new one.
         */

        if (new_interval != 0)
                cbuff_width = new_interval;

	cumulative_buffer_unlock(s);

#endif  /* ETAP_LOCK_ACCUMULATE */
}

#endif	/* ETAP */

/*
 *  ROUTINE:	etap_probe			[exported]
 *
 *  FUNCTION:	The etap_probe system call serves as a user-level probe,
 *		allowing user-level code to store event data into
 *		the monitored buffer(s).  
 */

kern_return_t
etap_probe(
	unsigned short	event_type,
	unsigned short	event_id,
	unsigned int	data_size,	/* total size in bytes */
	etap_data_t	*data)
{

#if    	ETAP_MONITOR

        mbuff_entry_t	mbuff_entryp;
	int 		cpu;
	int		free;
	spl_t		s;


	if (data_size > ETAP_DATA_SIZE)
		return (KERN_INVALID_ARGUMENT);

	if (event_table[event_type].status == ETAP_TRACE_OFF ||
		event_table[event_type].event != event_type)
		return (KERN_NO_ACCESS);

	mp_disable_preemption();
        cpu  = cpu_number();
	s    = splhigh();

        free = mbuff[cpu]->free;
	mbuff_entryp = &mbuff[cpu]->entry[free];

	/*
	 *  Load monitor buffer entry
	 */

	ETAP_TIMESTAMP(mbuff_entryp->time);
	mbuff_entryp->event    = event_id;
        mbuff_entryp->flags    = USER_EVENT;
	mbuff_entryp->instance = (u_int) current_thread();
	mbuff_entryp->pc       = 0;

	if (data != ETAP_DATA_NULL)
		copyin((const char *) data,
		       (char *) mbuff_entryp->data,
		       data_size);

	mbuff[cpu]->free = (free+1) % mbuff_entries;

	if (mbuff[cpu]->free == 0)
                mbuff[cpu]->timestamp++;

	splx(s);
	mp_enable_preemption();

	return (KERN_SUCCESS);

#else   /* ETAP_MONITOR */
        return (KERN_FAILURE);
#endif  /* ETAP_MONITOR */
}

/*
 *  ROUTINE:	etap_trace_thread		[exported]
 *
 *  FUNCTION:	Toggles thread's ETAP trace status bit.
 */

kern_return_t
etap_trace_thread(
	thread_act_t	thr_act,
	boolean_t	trace_status)
{
#if	ETAP_EVENT_MONITOR

	thread_t	thread;
	boolean_t	old_status;
	etap_data_t	probe_data;
	spl_t		s;

	if (thr_act == THR_ACT_NULL)
		return (KERN_INVALID_ARGUMENT);

	thread = act_lock_thread(thr_act);

	if (thread == THREAD_NULL) {
		act_unlock_thread(thr_act);
		return (KERN_INVALID_ARGUMENT);
	}

	s = splsched();
	thread_lock(thread);

	old_status = thread->etap_trace;
	thread->etap_trace = trace_status;

	ETAP_DATA_LOAD(probe_data[0],thr_act->task);
	ETAP_DATA_LOAD(probe_data[1],thr_act);
	ETAP_DATA_LOAD(probe_data[2],thread->sched_pri);

	thread_unlock(thread);
	splx(s);

	act_unlock_thread(thr_act);

	/*
	 *  Thread creation (ETAP_P_THREAD_LIFE: BEGIN) is ONLY recorded
	 *  here since a threads trace status is disabled by default.
	 */
	if (trace_status == TRUE && old_status == FALSE) {
		ETAP_PROBE_DATA(ETAP_P_THREAD_LIFE,
				EVENT_BEGIN,
				thread,
				&probe_data,
				ETAP_DATA_ENTRY*3);
	}

	/*
	 *  Thread termination is (falsely) recorded here if the trace
	 *  status has been disabled.  This event is recorded to allow
	 *  users the option of tracing a portion of a threads execution.
	 */
	if (trace_status == FALSE && old_status == TRUE) {
		ETAP_PROBE_DATA(ETAP_P_THREAD_LIFE,
				EVENT_END,
				thread,
				&probe_data,
				ETAP_DATA_ENTRY*3);
	}

	return (KERN_SUCCESS);

#else	/* ETAP_EVENT_MONITOR */
	return (KERN_FAILURE);
#endif	/* ETAP_EVENT_MONITOR */
}

/*
 *	ROUTINE:	etap_mon_reconfig		[exported]
 *
 *	FUNCTION:	Reallocates monitor buffers to hold specified number
 *			of entries.
 *
 *	NOTES:		In multiprocessor (SMP) case, a lock needs to be added
 *			here and in data collection macros to protect access
 *			to mbuff_entries.
 */
kern_return_t
etap_mon_reconfig(
	host_priv_t  host_priv,
	int	nentries)
{
#if	ETAP_EVENT_MONITOR
	struct monitor_buffer *nmbuff[NCPUS], *ombuff[NCPUS];
	int s, size, osize, i, ret;

	if (host_priv == HOST_PRIV_NULL)
		return KERN_INVALID_ARGUMENT;

	if (nentries <= 0)	/* must be at least 1 */
		return (KERN_FAILURE);

	size = ((nentries-1) * sizeof(struct mbuff_entry)) +
		sizeof(struct monitor_buffer);

	for (i = 0; i < NCPUS; ++i) {
		ret = kmem_alloc(kernel_map,
			(vm_offset_t *)&nmbuff[i],
			size);
		if (ret != KERN_SUCCESS) {
			if (i > 0) {
				int j;

				for (j = 0; j < i; ++j) {
					kmem_free(kernel_map,
						(vm_offset_t)nmbuff[j],
						size);
				}
			}
			return (ret);
		}
		bzero((char *) nmbuff[i], size);
	}
	osize = ((mbuff_entries-1) * sizeof (struct mbuff_entry)) +
		sizeof (struct monitor_buffer);

	s = splhigh();
	event_table_lock();
	for (i = 0; i < NCPUS; ++i) {
		ombuff[i] = mbuff[i];
		mbuff[i] = nmbuff[i];
	}
	mbuff_entries = nentries;
	event_table_unlock();
	splx(s);

	for (i = 0; i < NCPUS; ++i) {
		kmem_free(kernel_map,
			(vm_offset_t)ombuff[i],
			osize);
	}
	return (KERN_SUCCESS);
#else
	return (KERN_FAILURE);
#endif	/* ETAP_MONITOR */
}

/*
 *      ROUTINE:        etap_new_probe               [exported]
 *
 *      FUNCTION:       Reallocates monitor probe table, adding a new entry
 *
 */
kern_return_t
etap_new_probe(
	host_priv_t host_priv,
	vm_address_t name,
	vm_size_t namlen,
	boolean_t trace_on,
	vm_address_t id)
{
#if ETAP_EVENT_MONITOR
	event_table_t newtable, oldtable;
	unsigned short i, nid;
	int s;
	vm_size_t newsize = (event_table_max + 1) *
		sizeof (struct event_table_entry);
	boolean_t duplicate_name = FALSE;
	kern_return_t ret;

	if (host_priv == HOST_PRIV_NULL)
		return KERN_INVALID_ARGUMENT;

	if (namlen > EVENT_NAME_LENGTH - 1)
		return (KERN_INVALID_ARGUMENT);

	if ((ret = kmem_alloc(kernel_map, (vm_address_t *)&newtable,
		newsize)) != KERN_SUCCESS)
		return (ret);

	bcopy((const char *)event_table, (char *)newtable, event_table_max *
		sizeof (struct event_table_entry));

	if (copyin((const char *)name,
		(char *)&newtable[event_table_max].name, namlen))
		return (KERN_INVALID_ADDRESS);

	newtable[event_table_max].name[EVENT_NAME_LENGTH - 1] = '\0';
	newtable[event_table_max].status = trace_on;
	newtable[event_table_max].dynamic = 0;

	for (nid = i = 0; i < event_table_max; ++i) {
		if (strcmp((char *)newtable[event_table_max].name,
			newtable[i].name) == 0) {
			duplicate_name = TRUE;
			printf("duplicate name\n");
		}
		nid = max(nid, newtable[i].event);
	}
	++nid;

	if (nid >= ETAP_NO_TRACE || duplicate_name == TRUE) {
		kmem_free(kernel_map, (vm_address_t)newtable, newsize);
		if (nid >= ETAP_NO_TRACE) {
			printf("KERN_RESOURCE_SHORTAGE\n");
			return (KERN_RESOURCE_SHORTAGE);
		}
		else {
			printf("KERN_NAME_EXISTS\n");
			return (KERN_NAME_EXISTS);
		}
	}

	newtable[event_table_max].event = nid;

	s = splhigh();
	event_table_lock();
	oldtable = event_table;
	event_table = newtable;
	++event_table_max;
	event_table_unlock();
	splx(s);

	if (oldtable != event_table_init)
		kmem_free(kernel_map, (vm_address_t)oldtable,
			(event_table_max - 1) *
			sizeof (struct event_table_entry));

	*(unsigned short *)id = nid;

	return (KERN_SUCCESS);
#else
	return (KERN_FAILURE);
#endif	/* ETAP_EVENT_MONITOR */

}
/*
 *  ETAP trap probe hooks
 */

void
etap_interrupt_probe(int interrupt, int flag_setting)
{
	u_short	 flag;

	if (flag_setting == 1)
		flag = EVENT_BEGIN;
	else
		flag = EVENT_END;

	ETAP_PROBE_DATA_COND(ETAP_P_INTERRUPT,
			flag,
			current_thread(),
			&interrupt,
			sizeof(int),
			1);
}

void
etap_machcall_probe1(int syscall)
{
	ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
			EVENT_BEGIN | SYSCALL_TRAP,
			current_thread(),
			&syscall,
			sizeof(int));
}	

void
etap_machcall_probe2(void)
{
	ETAP_PROBE_DATA(ETAP_P_SYSCALL_MACH,
			EVENT_END | SYSCALL_TRAP,
			current_thread(),
			0,
			0);
}	

static void print_user_event(mbuff_entry_t);
static void print_kernel_event(mbuff_entry_t, boolean_t);
static void print_lock_event(mbuff_entry_t, const char *);

#if MACH_KDB
void db_show_etap_log(db_expr_t, boolean_t, db_expr_t, char *);
/*
 *
 *  ROUTINE:    etap_print                   [internal]
 *
 *  FUNCTION:   print each mbuff table (for use in debugger)
 *
 */
void
db_show_etap_log(
	db_expr_t	addr,
	boolean_t	have_addr,
	db_expr_t	count,
	char *		modif)
{
#if ETAP_MONITOR
	int cpu = cpu_number(), last, i, first, step, end, restart;
	boolean_t show_data = FALSE;

	last = (mbuff[cpu]->free - 1) % mbuff_entries;

	if(db_option(modif, 'r')) {
		first = last;
		step = -1;
		end = -1;
		restart = mbuff_entries - 1;
	} else {
		first = last + 1;
		step = 1;
		end = mbuff_entries;
		restart = 0;
	}

	if(db_option(modif, 'd'))
		show_data = TRUE;

	for(i = first; i != end; i += step) {
		if (mbuff[cpu]->entry[i].flags & USER_EVENT)
			print_user_event(&mbuff[cpu]->entry[i]);
		else
			print_kernel_event(&mbuff[cpu]->entry[i], show_data);
	}	
	for(i = restart; i != first; i += step) {
		if (mbuff[cpu]->entry[i].flags & USER_EVENT)
			print_user_event(&mbuff[cpu]->entry[i]);
		else
			print_kernel_event(&mbuff[cpu]->entry[i], show_data);
	}	
#else
	printf("ETAP event monitor not configured\n");
#endif	/* ETAP_MONITOR */
}

#if ETAP_MONITOR
static
void
print_user_event(mbuff_entry_t record)
{
        char *s, buf[256];

	db_printf("%x: %x%08x: ", record->instance, record->time.tv_sec,
		record->time.tv_nsec);
        switch (record->pc)
                {
        case ETAP_P_USER_EVENT0: s = "0"; break;
        case ETAP_P_USER_EVENT1: s = "1"; break;
        case ETAP_P_USER_EVENT2: s = "2"; break;
        case ETAP_P_USER_EVENT3: s = "3"; break;
        case ETAP_P_USER_EVENT4: s = "4"; break;
        case ETAP_P_USER_EVENT5: s = "5"; break;
        case ETAP_P_USER_EVENT6: s = "6"; break;
        case ETAP_P_USER_EVENT7: s = "7"; break;
        case ETAP_P_USER_EVENT8: s = "8"; break;
        case ETAP_P_USER_EVENT9: s = "9"; break;
        case ETAP_P_USER_EVENT10: s = "10"; break;
        case ETAP_P_USER_EVENT11: s = "11"; break;
        case ETAP_P_USER_EVENT12: s = "12"; break;
        case ETAP_P_USER_EVENT13: s = "13"; break;
        case ETAP_P_USER_EVENT14: s = "14"; break;
        case ETAP_P_USER_EVENT15: s = "15"; break;
        case ETAP_P_USER_EVENT16: s = "16"; break;
        case ETAP_P_USER_EVENT17: s = "17"; break;
        case ETAP_P_USER_EVENT18: s = "18"; break;
        case ETAP_P_USER_EVENT19: s = "19"; break;
        case ETAP_P_USER_EVENT20: s = "20"; break;
        case ETAP_P_USER_EVENT21: s = "21"; break;
        case ETAP_P_USER_EVENT22: s = "22"; break;
        case ETAP_P_USER_EVENT23: s = "23"; break;
        case ETAP_P_USER_EVENT24: s = "24"; break;
        case ETAP_P_USER_EVENT25: s = "25"; break;
        case ETAP_P_USER_EVENT26: s = "26"; break;
        case ETAP_P_USER_EVENT27: s = "27"; break;
        case ETAP_P_USER_EVENT28: s = "28"; break;
        case ETAP_P_USER_EVENT29: s = "29"; break;
        case ETAP_P_USER_EVENT30: s = "30"; break;
        case ETAP_P_USER_EVENT31: s = "31"; break;
        default:
		sprintf(buf, "dynamic %x", record->pc);
		s = buf;
		break;
                }

        db_printf("user probe %s: [%x] data = %x %x %x %x\n",
               s,
               record->event,
               record->data[0],
               record->data[1],
               record->data[2],
               record->data[3]);
}

static
void
print_kernel_event(mbuff_entry_t record, boolean_t data)
{
	char *text_name;
	int i;

	/* assume zero event means that record was never written to */
	if(record->event == 0)
		return;

	db_printf("%x: %x%08x: ", record->instance, record->time.tv_sec,
		record->time.tv_nsec);

        switch (record->event) {

        case    ETAP_P_THREAD_LIFE :
                if (record->flags & EVENT_BEGIN)
                        db_printf("thread created    [T:%x A:%x] P:%d\n",
                               record->data[0],
                               record->data[1],
                               record->data[2]);
                else
                        db_printf("thread terminated [T:%x A:%x] P:%d\n",
                               record->data[0],
                               record->data[1],
                               record->data[2]);
                break;

        case    ETAP_P_SYSCALL_MACH :
                if (record->flags & SYSCALL_TRAP)
                        text_name = system_table_lookup(SYS_TABLE_MACH_TRAP,
                                                        record->data[0]);
                else
                        text_name = system_table_lookup(SYS_TABLE_MACH_MESSAGE,
                                                        record->data[0]);

                if (record->flags & EVENT_BEGIN)
                        db_printf("mach enter: %s [%x]\n",
			       text_name,
                               record->data[0]);
                else
                        db_printf("mach exit :\n");
                break;

        case    ETAP_P_SYSCALL_UNIX :
                text_name = system_table_lookup(SYS_TABLE_UNIX_SYSCALL,
                                                record->data[0]);

                if (record->flags & EVENT_BEGIN)
                        db_printf("unix enter: %s\n", text_name);
                else
                        db_printf("unix exit : %s\n", text_name);
                break;

        case    ETAP_P_THREAD_CTX :
                if (record->flags & EVENT_END)
                        db_printf("context switch to   %x   ",
                               record->data[0]);
                else    /* EVENT_BEGIN */
                        db_printf("context switch from %x   ",
                               record->data[0]);

                switch (record->data[1]) {
                        case    BLOCKED_ON_SEMAPHORE :
                                db_printf("R: semaphore\n"); break;
                        case    BLOCKED_ON_LOCK :
                                db_printf("R: lock\n"); break;
                        case    BLOCKED_ON_MUTEX_LOCK :
                                db_printf("R: mutex lock\n"); break;
                        case    BLOCKED_ON_COMPLEX_LOCK :
                                db_printf("R: complex lock\n"); break;
                        case    BLOCKED_ON_PORT_RCV :
                                db_printf("R: port receive\n"); break;
                        case    BLOCKED_ON_REAPER_DONE :
                                db_printf("R: reaper thread done\n"); break;
                        case    BLOCKED_ON_IDLE_DONE :
                                db_printf("R: idle thread done\n"); break;
                        case    BLOCKED_ON_TERMINATION :
                                db_printf("R: termination\n"); break;
                        default :
                                if (record->data[2])
                                        db_printf("R: ast %x\n", record->data[2]);
                                else
                                        db_printf("R: undefined block\n");
                        };
                break;

        case    ETAP_P_INTERRUPT :
                if (record->flags & EVENT_BEGIN) {
                        text_name = system_table_lookup(SYS_TABLE_INTERRUPT,
                                                        record->data[0]);
                        db_printf("intr enter: %s\n", text_name);
                } else
                        db_printf("intr exit\n");
                break;

        case    ETAP_P_ACT_ABORT :
                db_printf("activation abort [A %x : S %x]\n",
                       record->data[1],

                       record->data[0]);
                break;

        case    ETAP_P_PRIORITY :
                db_printf("priority changed for %x   N:%d O:%d\n",
                       record->data[0],
                       record->data[1],
                       record->data[2]);
                break;

        case    ETAP_P_EXCEPTION :
                text_name = system_table_lookup(SYS_TABLE_EXCEPTION,
                                                record->data[0]);
                db_printf("exception: %s\n", text_name);
                break;

        case    ETAP_P_DEPRESSION :
                if (record->flags & EVENT_BEGIN)
                        db_printf("priority depressed\n");
                else {
                        if (record->data[0] == 0)
                            db_printf("priority undepressed : timed out\n");
                        else
                            db_printf("priority undepressed : self inflicted\n");
                }
                break;

        case    ETAP_P_MISC :
                db_printf("flags: %x data: %x %x %x %x\n", record->flags,
                        record->data[0], record->data[1], record->data[2],
                        record->data[3]);
                break;

        case    ETAP_P_DETAP :
                printf("flags: %x rtc: %x %09x dtime: %x %09x\n",
                       record->flags, record->data[0], record->data[1],
                       record->data[2], record->data[3]);
                break;

        default:
		for(i = 0; event_table_init[i].event != ETAP_NO_TRACE; ++i)
			if(record->event == event_table_init[i].event) {
				print_lock_event(record, event_table_init[i].name);
				return;
			}
               	db_printf("Unknown event: %d\n", record->event);
		break;
        }
	if(data)
		db_printf("    Data: %08x  %08x  %08x  %08x\n", record->data[0],
			record->data[1], record->data[2], record->data[3]);
}

void print_lock_event(mbuff_entry_t record, const char *name)
{
	char      *sym1, *sym2;
	db_addr_t offset1, offset2;

	db_find_sym_and_offset(record->data[0], &sym1, &offset1);

	db_printf("%15s", name);
	if (record->flags & SPIN_LOCK)
		printf(" spin  ");
	else if (record->flags & READ_LOCK)
		printf(" read  ");
	else if (record->flags & WRITE_LOCK)
		printf(" write ");
	else
		printf(" undef ");

	if (record->flags & ETAP_CONTENTION) {
		db_printf("wait  lock %s+%x\n",
			sym1, offset1);
	}
	else if (record->flags & ETAP_DURATION) {
		db_find_sym_and_offset(record->data[1], &sym2, &offset2);
		db_printf("lock %x+%x  unlock %x+%x\n",
			sym1, offset1, sym2, offset2);
	} else {
		db_printf("illegal op: neither HOLD or WAIT are specified\n");
	}

}

char *
system_table_lookup(unsigned int table, unsigned int number)
{
        int x;
        char *name = NULL;
	unsigned int offset;

        switch (table) {
        case SYS_TABLE_MACH_TRAP:
                name = mach_trap_name(number >> 4);
                break;
        case SYS_TABLE_MACH_MESSAGE:
                for (x=0; x < mach_message_table_entries; x++) {
                        if (mach_message_table[x].number == number) {
                                name = mach_message_table[x].name;
                                break;
                        }
                }
                break;
        case SYS_TABLE_UNIX_SYSCALL:
                number = -number;
                name = syscall_name(number);
                break;
        case SYS_TABLE_INTERRUPT:
		db_find_sym_and_offset((int)ivect[number], &name, &offset);
                break;
        case SYS_TABLE_EXCEPTION:
                name = exception_name(number);
                break;
        }
        return (name != NULL) ? name : "undefined";
}

#endif  /* MACH_KDB */
#endif	/* ETAP_MONITOR */
