X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/43866e378188c25dd1e2208016ab3cbeb086ae6c..36401178fd6817c043cc00b0c00c7f723e58efae:/bsd/kern/kdebug.c diff --git a/bsd/kern/kdebug.c b/bsd/kern/kdebug.c index cee34d326..952d2b87c 100644 --- a/bsd/kern/kdebug.c +++ b/bsd/kern/kdebug.c @@ -1,67 +1,123 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @Apple_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * The contents of this file constitute Original Code as defined in and + * are subject to the Apple Public Source License Version 1.1 (the + * "License"). You may not use this file except in compliance with the + * License. Please obtain a copy of the License at + * http://www.apple.com/publicsource and read it before using this file. * - * 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 + * This 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. + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ #include -#define HZ 100 -#include -#include -#include - -#include #include #include -#include +#include +#include #include #include +#include +#include + +#define HZ 100 +#include +#include +#include +#include #include #include +#include #include #include +#include +#include + +#include /* for host_info() */ +#include + +/* XXX should have prototypes, but Mach does not provide one */ +void task_act_iterate_wth_args(task_t, void(*)(thread_t, void *), void *); +int cpu_number(void); /* XXX include path broken */ + +/* XXX should probably be static, but it's debugging code... */ +int kdbg_read(user_addr_t, size_t *); +void kdbg_control_chud(int, void *); +int kdbg_control(int *, u_int, user_addr_t, size_t *); +int kdbg_getentropy (user_addr_t, size_t *, int); +int kdbg_readmap(user_addr_t, size_t *); +int kdbg_getreg(kd_regtype *); +int kdbg_setreg(kd_regtype *); +int kdbg_setrtcdec(kd_regtype *); +int kdbg_setpidex(kd_regtype *); +int kdbg_setpid(kd_regtype *); +void kdbg_mapinit(void); +int kdbg_reinit(void); +int kdbg_bootstrap(void); + +static int create_buffers(void); +static void delete_buffers(void); + +extern void IOSleep(int); + +#ifdef ppc +extern uint32_t maxDec; +#endif + /* trace enable status */ unsigned int kdebug_enable = 0; /* track timestamps for security server's entropy needs */ -mach_timespec_t * kd_entropy_buffer = 0; +uint64_t * kd_entropy_buffer = 0; unsigned int kd_entropy_bufsize = 0; unsigned int kd_entropy_count = 0; unsigned int kd_entropy_indx = 0; unsigned int kd_entropy_buftomem = 0; -/* kd_buf kd_buffer[kd_bufsize/sizeof(kd_buf)]; */ -kd_buf * kd_bufptr; -unsigned int kd_buftomem=0; -kd_buf * kd_buffer=0; -kd_buf * kd_buflast; -kd_buf * kd_readlast; + +#define SLOW_NOLOG 0x01 +#define SLOW_CHECKS 0x02 +#define SLOW_ENTROPY 0x04 + +unsigned int kdebug_slowcheck=SLOW_NOLOG; + +unsigned int kd_cpus; + +struct kd_bufinfo { + kd_buf * kd_stop; + kd_buf * kd_bufptr; + kd_buf * kd_buffer; + kd_buf * kd_buflast; + kd_buf * kd_readlast; + int kd_wrapped; /* plus, the global flag KDBG_WRAPPED is set if one of the buffers has wrapped */ + uint64_t kd_prev_timebase; + int kd_pad[24]; /* pad out to 128 bytes so that no cache line is shared between CPUs */ + +}; + +struct kd_bufinfo *kdbip = NULL; + +#define KDCOPYBUF_COUNT 1024 +#define KDCOPYBUF_SIZE (KDCOPYBUF_COUNT * sizeof(kd_buf)) +kd_buf *kdcopybuf = NULL; + + unsigned int nkdbufs = 8192; unsigned int kd_bufsize = 0; unsigned int kdebug_flags = 0; -unsigned int kdebug_nolog=1; unsigned int kdlog_beg=0; unsigned int kdlog_end=0; unsigned int kdlog_value1=0; @@ -69,8 +125,28 @@ unsigned int kdlog_value2=0; unsigned int kdlog_value3=0; unsigned int kdlog_value4=0; -unsigned long long kd_prev_timebase = 0LL; -decl_simple_lock_data(,kd_trace_lock); +static lck_mtx_t * kd_trace_mtx_sysctl; +static lck_grp_t * kd_trace_mtx_sysctl_grp; +static lck_attr_t * kd_trace_mtx_sysctl_attr; +static lck_grp_attr_t *kd_trace_mtx_sysctl_grp_attr; + +static lck_grp_t *stackshot_subsys_lck_grp; +static lck_grp_attr_t *stackshot_subsys_lck_grp_attr; +static lck_attr_t *stackshot_subsys_lck_attr; +static lck_mtx_t stackshot_subsys_mutex; + +void *stackshot_snapbuf = NULL; + +int +stack_snapshot2(pid_t pid, user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t options, register_t *retval); + +extern void +kdp_snapshot_preflight(int pid, void *tracebuf, uint32_t tracebuf_size, uint32_t options); + +extern int +kdp_stack_snapshot_geterror(void); +extern unsigned int +kdp_stack_snapshot_bytes_traced(void); kd_threadmap *kd_mapptr = 0; unsigned int kd_mapsize = 0; @@ -81,23 +157,11 @@ pid_t global_state_pid = -1; /* Used to control exclusive use of kd_buffer #define DBG_FUNC_MASK 0xfffffffc -#ifdef ppc -extern natural_t rtclock_decrementer_min; -#endif /* ppc */ - -struct kdebug_args { - int code; - int arg1; - int arg2; - int arg3; - int arg4; - int arg5; -}; - /* task to string structure */ struct tts { - task_t *task; + task_t task; /* from procs task */ + pid_t pid; /* from procs p_pid */ char task_comm[20]; /* from procs p_comm */ }; @@ -120,46 +184,110 @@ typedef void (*kd_chudhook_fn) (unsigned int debugid, unsigned int arg1, kd_chudhook_fn kdebug_chudhook = 0; /* pointer to CHUD toolkit function */ +__private_extern__ void stackshot_lock_init( void ) __attribute__((section("__TEXT, initcode"))); + /* Support syscall SYS_kdebug_trace */ -kdebug_trace(p, uap, retval) - struct proc *p; - struct kdebug_args *uap; - register_t *retval; +int +kdebug_trace(__unused struct proc *p, struct kdebug_trace_args *uap, __unused register_t *retval) { - if (kdebug_nolog) - return(EINVAL); + if ( (kdebug_enable == 0) ) + return(EINVAL); - kernel_debug(uap->code, uap->arg1, uap->arg2, uap->arg3, uap->arg4, 0); - return(0); + kernel_debug(uap->code, uap->arg1, uap->arg2, uap->arg3, uap->arg4, 0); + return(0); } +static int +create_buffers(void) +{ + unsigned int cpu, i; + int nentries; -void -kernel_debug(debugid, arg1, arg2, arg3, arg4, arg5) -unsigned int debugid, arg1, arg2, arg3, arg4, arg5; + nentries = nkdbufs / kd_cpus; + nkdbufs = nentries * kd_cpus; + + kd_bufsize = nentries * sizeof(kd_buf); + + bzero((char *)kdbip, sizeof(struct kd_bufinfo) * kd_cpus); + + if (kdcopybuf == 0) { + if (kmem_alloc(kernel_map, (unsigned int *)&kdcopybuf, (vm_size_t)KDCOPYBUF_SIZE) != KERN_SUCCESS) + return(ENOMEM); + } + for (cpu = 0; cpu < kd_cpus; cpu++) { + if (kmem_alloc(kernel_map, (unsigned int *)&kdbip[cpu].kd_buffer, kd_bufsize) != KERN_SUCCESS) + break; + } + if (cpu < kd_cpus) { + for (i = 0; i < cpu; i++) + kmem_free(kernel_map, (vm_offset_t)kdbip[i].kd_buffer, kd_bufsize); + kd_bufsize = 0; + + kmem_free(kernel_map, (vm_offset_t)kdcopybuf, KDCOPYBUF_SIZE); + kdcopybuf = NULL; + + return(ENOMEM); + } + for (cpu = 0; cpu < kd_cpus; cpu++) { + kdbip[cpu].kd_bufptr = kdbip[cpu].kd_buffer; + kdbip[cpu].kd_buflast = &kdbip[cpu].kd_bufptr[nentries]; + kdbip[cpu].kd_readlast = kdbip[cpu].kd_bufptr; + } + kdebug_flags |= KDBG_BUFINIT; + + return(0); +} + + +static void +delete_buffers(void) { + unsigned int cpu; + + if (kd_bufsize && (kdebug_flags & KDBG_BUFINIT)) { + for (cpu = 0; cpu < kd_cpus; cpu++) + kmem_free(kernel_map, (vm_offset_t)kdbip[cpu].kd_buffer, kd_bufsize); + kd_bufsize = 0; + } + if (kdcopybuf) { + kmem_free(kernel_map, (vm_offset_t)kdcopybuf, KDCOPYBUF_SIZE); + kdcopybuf = NULL; + } + kdebug_flags &= ~KDBG_BUFINIT; +} + + +static void +kernel_debug_internal(unsigned int debugid, unsigned int arg1, unsigned int arg2, unsigned int arg3, + unsigned int arg4, unsigned int arg5, int entropy_flag) +{ + int s; kd_buf * kd; struct proc *curproc; - int s; unsigned long long now; - mach_timespec_t *tsp; + int cpu; + + s = ml_set_interrupts_enabled(FALSE); + + now = mach_absolute_time() & KDBG_TIMESTAMP_MASK; + cpu = cpu_number(); if (kdebug_enable & KDEBUG_ENABLE_CHUD) { - if (kdebug_chudhook) - kdebug_chudhook(debugid, arg1, arg2, arg3, arg4, arg5); + if (kdebug_chudhook) + kdebug_chudhook(debugid, arg1, arg2, arg3, arg4, arg5); - if (!((kdebug_enable & KDEBUG_ENABLE_ENTROPY) || - (kdebug_enable & KDEBUG_ENABLE_TRACE))) - return; + if ( !(kdebug_enable & (KDEBUG_ENABLE_ENTROPY | KDEBUG_ENABLE_TRACE))) + goto out; } - s = ml_set_interrupts_enabled(FALSE); + if (kdebug_slowcheck == 0) + goto record_trace; - if (kdebug_enable & KDEBUG_ENABLE_ENTROPY) + if (entropy_flag && (kdebug_enable & KDEBUG_ENABLE_ENTROPY)) { if (kd_entropy_indx < kd_entropy_count) { - ml_get_timebase((unsigned long long *) &kd_entropy_buffer [ kd_entropy_indx]); + kd_entropy_buffer [ kd_entropy_indx] = mach_absolute_time(); kd_entropy_indx++; } @@ -167,50 +295,35 @@ unsigned int debugid, arg1, arg2, arg3, arg4, arg5; { /* Disable entropy collection */ kdebug_enable &= ~KDEBUG_ENABLE_ENTROPY; + kdebug_slowcheck &= ~SLOW_ENTROPY; } } - if (kdebug_nolog) - { - ml_set_interrupts_enabled(s); - return; - } - - usimple_lock(&kd_trace_lock); + if ( (kdebug_slowcheck & SLOW_NOLOG) ) + goto out; + if (kdebug_flags & KDBG_PIDCHECK) { /* If kdebug flag is not set for current proc, return */ curproc = current_proc(); - if ((curproc && !(curproc->p_flag & P_KDEBUG)) && + if ((curproc && !(curproc->p_kdebug)) && ((debugid&0xffff0000) != (MACHDBG_CODE(DBG_MACH_SCHED, 0) | DBG_FUNC_NONE))) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); - return; - } + goto out; } else if (kdebug_flags & KDBG_PIDEXCLUDE) { /* If kdebug flag is set for current proc, return */ curproc = current_proc(); - if ((curproc && (curproc->p_flag & P_KDEBUG)) && + if ((curproc && curproc->p_kdebug) && ((debugid&0xffff0000) != (MACHDBG_CODE(DBG_MACH_SCHED, 0) | DBG_FUNC_NONE))) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); - return; - } + goto out; } if (kdebug_flags & KDBG_RANGECHECK) { - if ((debugid < kdlog_beg) || (debugid > kdlog_end) - && (debugid >> 24 != DBG_TRACE)) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); - return; - } + if ((debugid < kdlog_beg) + || ((debugid >= kdlog_end) && (debugid >> 24 != DBG_TRACE))) + goto out; } else if (kdebug_flags & KDBG_VALCHECK) { @@ -219,231 +332,166 @@ unsigned int debugid, arg1, arg2, arg3, arg4, arg5; (debugid & DBG_FUNC_MASK) != kdlog_value3 && (debugid & DBG_FUNC_MASK) != kdlog_value4 && (debugid >> 24 != DBG_TRACE)) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); - return; - } + goto out; } - kd = kd_bufptr; + +record_trace: + kd = kdbip[cpu].kd_bufptr; kd->debugid = debugid; kd->arg1 = arg1; kd->arg2 = arg2; kd->arg3 = arg3; kd->arg4 = arg4; - kd->arg5 = (int)current_thread(); - if (cpu_number()) - kd->arg5 |= KDBG_CPU_MASK; + kd->arg5 = arg5; - ml_get_timebase((unsigned long long *)&kd->timestamp); - - /* Watch for out of order timestamps */ - now = (((unsigned long long)kd->timestamp.tv_sec) << 32) | - (unsigned long long)((unsigned int)(kd->timestamp.tv_nsec)); - - if (now < kd_prev_timebase) + /* + * Watch for out of order timestamps + */ + if (now < kdbip[cpu].kd_prev_timebase) { - /* timestamps are out of order -- adjust */ - kd_prev_timebase++; - tsp = (mach_timespec_t *)&kd_prev_timebase; - kd->timestamp.tv_sec = tsp->tv_sec; - kd->timestamp.tv_nsec = tsp->tv_nsec; + /* + * if so, just store the previous timestamp + a cycle + */ + now = ++kdbip[cpu].kd_prev_timebase & KDBG_TIMESTAMP_MASK; } else { - /* Then just store the previous timestamp */ - kd_prev_timebase = now; + kdbip[cpu].kd_prev_timebase = now; } + kd->timestamp = now | (((uint64_t)cpu) << KDBG_CPU_SHIFT); + kdbip[cpu].kd_bufptr++; - kd_bufptr++; + if (kdbip[cpu].kd_bufptr >= kdbip[cpu].kd_buflast) + kdbip[cpu].kd_bufptr = kdbip[cpu].kd_buffer; - if (kd_bufptr >= kd_buflast) - kd_bufptr = kd_buffer; - if (kd_bufptr == kd_readlast) { + if (kdbip[cpu].kd_bufptr == kdbip[cpu].kd_readlast) { if (kdebug_flags & KDBG_NOWRAP) - kdebug_nolog = 1; + kdebug_slowcheck |= SLOW_NOLOG; + kdbip[cpu].kd_wrapped = 1; kdebug_flags |= KDBG_WRAPPED; } - usimple_unlock(&kd_trace_lock); + +out: ml_set_interrupts_enabled(s); } void -kernel_debug1(debugid, arg1, arg2, arg3, arg4, arg5) -unsigned int debugid, arg1, arg2, arg3, arg4, arg5; +kernel_debug(unsigned int debugid, unsigned int arg1, unsigned int arg2, unsigned int arg3, + unsigned int arg4, __unused unsigned int arg5) { - kd_buf * kd; - struct proc *curproc; - int s; - unsigned long long now; - mach_timespec_t *tsp; - - if (kdebug_enable & KDEBUG_ENABLE_CHUD) { - if (kdebug_chudhook) - (void)kdebug_chudhook(debugid, arg1, arg2, arg3, arg4, arg5); - - if (!((kdebug_enable & KDEBUG_ENABLE_ENTROPY) || - (kdebug_enable & KDEBUG_ENABLE_TRACE))) - return; - } + kernel_debug_internal(debugid, arg1, arg2, arg3, arg4, (int)current_thread(), 1); +} - s = ml_set_interrupts_enabled(FALSE); +void +kernel_debug1(unsigned int debugid, unsigned int arg1, unsigned int arg2, unsigned int arg3, + unsigned int arg4, unsigned int arg5) +{ + kernel_debug_internal(debugid, arg1, arg2, arg3, arg4, arg5, 0); +} - if (kdebug_nolog) - { - ml_set_interrupts_enabled(s); - return; - } +static void +kdbg_lock_init(void) +{ + host_basic_info_data_t hinfo; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - usimple_lock(&kd_trace_lock); - if (kdebug_flags & KDBG_PIDCHECK) - { - /* If kdebug flag is not set for current proc, return */ - curproc = current_proc(); - if ((curproc && !(curproc->p_flag & P_KDEBUG)) && - ((debugid&0xffff0000) != (MACHDBG_CODE(DBG_MACH_SCHED, 0) | DBG_FUNC_NONE))) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); - return; - } - } - else if (kdebug_flags & KDBG_PIDEXCLUDE) - { - /* If kdebug flag is set for current proc, return */ - curproc = current_proc(); - if ((curproc && (curproc->p_flag & P_KDEBUG)) && - ((debugid&0xffff0000) != (MACHDBG_CODE(DBG_MACH_SCHED, 0) | DBG_FUNC_NONE))) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); + if (kdebug_flags & KDBG_LOCKINIT) return; - } - } - if (kdebug_flags & KDBG_RANGECHECK) - { - if ((debugid < kdlog_beg) || (debugid > kdlog_end) - && (debugid >> 24 != DBG_TRACE)) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); - return; - } - } - else if (kdebug_flags & KDBG_VALCHECK) - { - if ((debugid & DBG_FUNC_MASK) != kdlog_value1 && - (debugid & DBG_FUNC_MASK) != kdlog_value2 && - (debugid & DBG_FUNC_MASK) != kdlog_value3 && - (debugid & DBG_FUNC_MASK) != kdlog_value4 && - (debugid >> 24 != DBG_TRACE)) - { - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); - return; - } - } + /* get the number of cpus and cache it */ +#define BSD_HOST 1 + host_info((host_t)BSD_HOST, HOST_BASIC_INFO, (host_info_t)&hinfo, &count); + kd_cpus = hinfo.physical_cpu_max; - kd = kd_bufptr; - kd->debugid = debugid; - kd->arg1 = arg1; - kd->arg2 = arg2; - kd->arg3 = arg3; - kd->arg4 = arg4; - kd->arg5 = arg5; - ml_get_timebase((unsigned long long *)&kd->timestamp); + if (kmem_alloc(kernel_map, (unsigned int *)&kdbip, + sizeof(struct kd_bufinfo) * kd_cpus) != KERN_SUCCESS) + return; - /* Watch for out of order timestamps */ - now = (((unsigned long long)kd->timestamp.tv_sec) << 32) | - (unsigned long long)((unsigned int)(kd->timestamp.tv_nsec)); + /* + * allocate lock group attribute and group + */ + kd_trace_mtx_sysctl_grp_attr = lck_grp_attr_alloc_init(); + kd_trace_mtx_sysctl_grp = lck_grp_alloc_init("kdebug", kd_trace_mtx_sysctl_grp_attr); + + /* + * allocate the lock attribute + */ + kd_trace_mtx_sysctl_attr = lck_attr_alloc_init(); - if (now < kd_prev_timebase) - { - /* timestamps are out of order -- adjust */ - kd_prev_timebase++; - tsp = (mach_timespec_t *)&kd_prev_timebase; - kd->timestamp.tv_sec = tsp->tv_sec; - kd->timestamp.tv_nsec = tsp->tv_nsec; - } - else - { - /* Then just store the previous timestamp */ - kd_prev_timebase = now; - } - kd_bufptr++; + /* + * allocate and initialize spin lock and mutex + */ + kd_trace_mtx_sysctl = lck_mtx_alloc_init(kd_trace_mtx_sysctl_grp, kd_trace_mtx_sysctl_attr); - if (kd_bufptr >= kd_buflast) - kd_bufptr = kd_buffer; - if (kd_bufptr == kd_readlast) { - if (kdebug_flags & KDBG_NOWRAP) - kdebug_nolog = 1; - kdebug_flags |= KDBG_WRAPPED; - } - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); + kdebug_flags |= KDBG_LOCKINIT; } -kdbg_bootstrap() +int +kdbg_bootstrap(void) { - kd_bufsize = nkdbufs * sizeof(kd_buf); - if (kmem_alloc(kernel_map, &kd_buftomem, - (vm_size_t)kd_bufsize) == KERN_SUCCESS) - kd_buffer = (kd_buf *) kd_buftomem; - else kd_buffer= (kd_buf *) 0; - kdebug_flags &= ~KDBG_WRAPPED; - if (kd_buffer) { - simple_lock_init(&kd_trace_lock); - kdebug_flags |= (KDBG_INIT | KDBG_BUFINIT); - kd_bufptr = kd_buffer; - kd_buflast = &kd_bufptr[nkdbufs]; - kd_readlast = kd_bufptr; - kd_prev_timebase = 0LL; - return(0); - } else { - kd_bufsize=0; - kdebug_flags &= ~(KDBG_INIT | KDBG_BUFINIT); - return(EINVAL); - } - + kdebug_flags &= ~KDBG_WRAPPED; + + return (create_buffers()); } -kdbg_reinit() +int +kdbg_reinit(void) { - int x; int ret=0; - /* Disable trace collecting */ + /* + * Disable trace collecting + * First make sure we're not in + * the middle of cutting a trace + */ + kdebug_enable &= ~KDEBUG_ENABLE_TRACE; - kdebug_nolog = 1; + kdebug_slowcheck |= SLOW_NOLOG; + + /* + * make sure the SLOW_NOLOG is seen + * by everyone that might be trying + * to cut a trace.. + */ + IOSleep(100); - if ((kdebug_flags & KDBG_INIT) && (kdebug_flags & KDBG_BUFINIT) && kd_bufsize && kd_buffer) - kmem_free(kernel_map, (char *)kd_buffer, kd_bufsize); + delete_buffers(); if ((kdebug_flags & KDBG_MAPINIT) && kd_mapsize && kd_mapptr) { - kmem_free(kernel_map, (char *)kd_mapptr, kd_mapsize); + kmem_free(kernel_map, (vm_offset_t)kd_mapptr, kd_mapsize); kdebug_flags &= ~KDBG_MAPINIT; kd_mapsize = 0; kd_mapptr = (kd_threadmap *) 0; kd_mapcount = 0; } - ret= kdbg_bootstrap(); + ret = kdbg_bootstrap(); return(ret); } -void kdbg_trace_string(struct proc *proc, long *arg1, long *arg2, long *arg3, long *arg4) +void +kdbg_trace_data(struct proc *proc, long *arg_pid) +{ + if (!proc) + *arg_pid = 0; + else + *arg_pid = proc->p_pid; + + return; +} + + +void +kdbg_trace_string(struct proc *proc, long *arg1, long *arg2, long *arg3, long *arg4) { - int i; char *dbg_nameptr; int dbg_namelen; long dbg_parms[4]; - if (!proc) { *arg1 = 0; @@ -461,15 +509,10 @@ void kdbg_trace_string(struct proc *proc, long *arg1, long *arg2, long *arg3, lo dbg_parms[2]=0L; dbg_parms[3]=0L; - if(dbg_namelen > sizeof(dbg_parms)) + if(dbg_namelen > (int)sizeof(dbg_parms)) dbg_namelen = sizeof(dbg_parms); - for(i=0;dbg_namelen > 0; i++) - { - dbg_parms[i]=*(long*)dbg_nameptr; - dbg_nameptr += sizeof(long); - dbg_namelen -= sizeof(long); - } + strncpy((char *)dbg_parms, dbg_nameptr, dbg_namelen); *arg1=dbg_parms[0]; *arg2=dbg_parms[1]; @@ -477,23 +520,34 @@ void kdbg_trace_string(struct proc *proc, long *arg1, long *arg2, long *arg3, lo *arg4=dbg_parms[3]; } -kdbg_resolve_map(thread_act_t th_act, krt_t *t) +static void +kdbg_resolve_map(thread_t th_act, void *opaque) { kd_threadmap *mapptr; + krt_t *t = (krt_t *)opaque; if(t->count < t->maxcount) { mapptr=&t->map[t->count]; - mapptr->thread = (unsigned int)getshuttle_thread(th_act); - mapptr->valid = 1; - (void) strncpy (mapptr->command, t->atts->task_comm, - sizeof(t->atts->task_comm)-1); - mapptr->command[sizeof(t->atts->task_comm)-1] = '\0'; + mapptr->thread = (unsigned int)th_act; + (void) strlcpy (mapptr->command, t->atts->task_comm, + sizeof(t->atts->task_comm)); + + /* + Some kernel threads have no associated pid. + We still need to mark the entry as valid. + */ + if (t->atts->pid) + mapptr->valid = t->atts->pid; + else + mapptr->valid = 1; + t->count++; } } -void kdbg_mapinit() +void +kdbg_mapinit(void) { struct proc *p; struct krt akrt; @@ -507,6 +561,9 @@ void kdbg_mapinit() if (kdebug_flags & KDBG_MAPINIT) return; + /* need to use PROC_SCANPROCLIST with proc_iterate */ + proc_list_lock(); + /* Calculate the sizes of map buffers*/ for (p = allproc.lh_first, kd_mapcount=0, tts_count=0; p; p = p->p_list.le_next) @@ -515,6 +572,8 @@ void kdbg_mapinit() tts_count++; } + proc_list_unlock(); + /* * The proc count could change during buffer allocation, * so introduce a small fudge factor to bump up the @@ -527,14 +586,20 @@ void kdbg_mapinit() kd_mapsize = kd_mapcount * sizeof(kd_threadmap); if((kmem_alloc(kernel_map, & kd_maptomem, (vm_size_t)kd_mapsize) == KERN_SUCCESS)) + { kd_mapptr = (kd_threadmap *) kd_maptomem; + bzero(kd_mapptr, kd_mapsize); + } else kd_mapptr = (kd_threadmap *) 0; tts_mapsize = tts_count * sizeof(struct tts); if((kmem_alloc(kernel_map, & tts_maptomem, (vm_size_t)tts_mapsize) == KERN_SUCCESS)) + { tts_mapptr = (struct tts *) tts_maptomem; + bzero(tts_mapptr, tts_mapsize); + } else tts_mapptr = (struct tts *) 0; @@ -546,18 +611,26 @@ void kdbg_mapinit() */ if (tts_mapptr) { + /* should use proc_iterate */ + proc_list_lock(); + for (p = allproc.lh_first, i=0; p && i < tts_count; p = p->p_list.le_next) { - if (p->p_flag & P_WEXIT) + if (p->p_lflag & P_LEXIT) continue; - if (task_reference_try(p->task)) { - tts_mapptr[i].task = p->task; - (void)strncpy(&tts_mapptr[i].task_comm, p->p_comm, sizeof(tts_mapptr[i].task_comm) - 1); - i++; + if (p->task) { + task_reference(p->task); + tts_mapptr[i].task = p->task; + tts_mapptr[i].pid = p->p_pid; + (void)strlcpy(tts_mapptr[i].task_comm, p->p_comm, sizeof(tts_mapptr[i].task_comm)); + i++; } } tts_count = i; + + proc_list_unlock(); + } @@ -573,37 +646,50 @@ void kdbg_mapinit() { akrt.atts = &tts_mapptr[i]; task_act_iterate_wth_args(tts_mapptr[i].task, kdbg_resolve_map, &akrt); - task_deallocate(tts_mapptr[i].task); + task_deallocate((task_t) tts_mapptr[i].task); } - kmem_free(kernel_map, (char *)tts_mapptr, tts_mapsize); + kmem_free(kernel_map, (vm_offset_t)tts_mapptr, tts_mapsize); } } -kdbg_clear() +static void +kdbg_clear(void) { -int x; + /* + * Clean up the trace buffer + * First make sure we're not in + * the middle of cutting a trace + */ - /* Clean up the trace buffer */ - global_state_pid = -1; kdebug_enable &= ~KDEBUG_ENABLE_TRACE; - kdebug_nolog = 1; - kdebug_flags &= ~KDBG_BUFINIT; + kdebug_slowcheck = SLOW_NOLOG; + + /* + * make sure the SLOW_NOLOG is seen + * by everyone that might be trying + * to cut a trace.. + */ + IOSleep(100); + + if (kdebug_enable & KDEBUG_ENABLE_ENTROPY) + kdebug_slowcheck |= SLOW_ENTROPY; + + global_state_pid = -1; kdebug_flags &= (unsigned int)~KDBG_CKTYPES; kdebug_flags &= ~(KDBG_NOWRAP | KDBG_RANGECHECK | KDBG_VALCHECK); kdebug_flags &= ~(KDBG_PIDCHECK | KDBG_PIDEXCLUDE); - kmem_free(kernel_map, (char *)kd_buffer, kd_bufsize); - kd_buffer = (kd_buf *)0; - kd_bufsize = 0; - kd_prev_timebase = 0LL; + + delete_buffers(); /* Clean up the thread map buffer */ kdebug_flags &= ~KDBG_MAPINIT; - kmem_free(kernel_map, (char *)kd_mapptr, kd_mapsize); + kmem_free(kernel_map, (vm_offset_t)kd_mapptr, kd_mapsize); kd_mapptr = (kd_threadmap *) 0; kd_mapsize = 0; kd_mapcount = 0; } +int kdbg_setpid(kd_regtype *kdr) { pid_t pid; @@ -615,7 +701,7 @@ kdbg_setpid(kd_regtype *kdr) if (pid > 0) { - if ((p = pfind(pid)) == NULL) + if ((p = proc_find(pid)) == NULL) ret = ESRCH; else { @@ -623,14 +709,17 @@ kdbg_setpid(kd_regtype *kdr) { kdebug_flags |= KDBG_PIDCHECK; kdebug_flags &= ~KDBG_PIDEXCLUDE; - p->p_flag |= P_KDEBUG; + kdebug_slowcheck |= SLOW_CHECKS; + + p->p_kdebug = 1; } else /* turn off pid check for this pid value */ { /* Don't turn off all pid checking though */ /* kdebug_flags &= ~KDBG_PIDCHECK;*/ - p->p_flag &= ~P_KDEBUG; + p->p_kdebug = 0; } + proc_rele(p); } } else @@ -639,6 +728,7 @@ kdbg_setpid(kd_regtype *kdr) } /* This is for pid exclusion in the trace buffer */ +int kdbg_setpidex(kd_regtype *kdr) { pid_t pid; @@ -650,7 +740,7 @@ kdbg_setpidex(kd_regtype *kdr) if (pid > 0) { - if ((p = pfind(pid)) == NULL) + if ((p = proc_find(pid)) == NULL) ret = ESRCH; else { @@ -658,14 +748,17 @@ kdbg_setpidex(kd_regtype *kdr) { kdebug_flags |= KDBG_PIDEXCLUDE; kdebug_flags &= ~KDBG_PIDCHECK; - p->p_flag |= P_KDEBUG; + kdebug_slowcheck |= SLOW_CHECKS; + + p->p_kdebug = 1; } else /* turn off pid exclusion for this pid value */ { /* Don't turn off all pid exclusion though */ /* kdebug_flags &= ~KDBG_PIDEXCLUDE;*/ - p->p_flag &= ~P_KDEBUG; + p->p_kdebug = 0; } + proc_rele(p); } } else @@ -673,7 +766,8 @@ kdbg_setpidex(kd_regtype *kdr) return(ret); } -/* This is for setting a minimum decrementer value */ +/* This is for setting a maximum decrementer value */ +int kdbg_setrtcdec(kd_regtype *kdr) { int ret=0; @@ -682,21 +776,23 @@ kdbg_setrtcdec(kd_regtype *kdr) decval = (natural_t)kdr->value1; if (decval && decval < KDBG_MINRTCDEC) - ret = EINVAL; + ret = EINVAL; #ifdef ppc - else - rtclock_decrementer_min = decval; + else { + maxDec = decval ? decval : 0x7FFFFFFF; /* Set or reset the max decrementer */ + } #else - else - ret = EOPNOTSUPP; + else + ret = ENOTSUP; #endif /* ppc */ return(ret); } +int kdbg_setreg(kd_regtype * kdr) { - int i,j, ret=0; + int ret=0; unsigned int val_1, val_2, val; switch (kdr->type) { @@ -708,6 +804,7 @@ kdbg_setreg(kd_regtype * kdr) kdebug_flags &= (unsigned int)~KDBG_CKTYPES; kdebug_flags &= ~KDBG_VALCHECK; /* Turn off specific value check */ kdebug_flags |= (KDBG_RANGECHECK | KDBG_CLASSTYPE); + kdebug_slowcheck |= SLOW_CHECKS; break; case KDBG_SUBCLSTYPE : val_1 = (kdr->value1 & 0xff); @@ -718,6 +815,7 @@ kdbg_setreg(kd_regtype * kdr) kdebug_flags &= (unsigned int)~KDBG_CKTYPES; kdebug_flags &= ~KDBG_VALCHECK; /* Turn off specific value check */ kdebug_flags |= (KDBG_RANGECHECK | KDBG_SUBCLSTYPE); + kdebug_slowcheck |= SLOW_CHECKS; break; case KDBG_RANGETYPE : kdlog_beg = (kdr->value1); @@ -725,6 +823,7 @@ kdbg_setreg(kd_regtype * kdr) kdebug_flags &= (unsigned int)~KDBG_CKTYPES; kdebug_flags &= ~KDBG_VALCHECK; /* Turn off specific value check */ kdebug_flags |= (KDBG_RANGECHECK | KDBG_RANGETYPE); + kdebug_slowcheck |= SLOW_CHECKS; break; case KDBG_VALCHECK: kdlog_value1 = (kdr->value1); @@ -734,9 +833,16 @@ kdbg_setreg(kd_regtype * kdr) kdebug_flags &= (unsigned int)~KDBG_CKTYPES; kdebug_flags &= ~KDBG_RANGECHECK; /* Turn off range check */ kdebug_flags |= KDBG_VALCHECK; /* Turn on specific value check */ + kdebug_slowcheck |= SLOW_CHECKS; break; case KDBG_TYPENONE : kdebug_flags &= (unsigned int)~KDBG_CKTYPES; + + if ( (kdebug_flags & (KDBG_RANGECHECK | KDBG_VALCHECK | KDBG_PIDCHECK | KDBG_PIDEXCLUDE)) ) + kdebug_slowcheck |= SLOW_CHECKS; + else + kdebug_slowcheck &= ~SLOW_CHECKS; + kdlog_beg = 0; kdlog_end = 0; break; @@ -747,11 +853,13 @@ kdbg_setreg(kd_regtype * kdr) return(ret); } -kdbg_getreg(kd_regtype * kdr) +int +kdbg_getreg(__unused kd_regtype * kdr) { +#if 0 int i,j, ret=0; unsigned int val_1, val_2, val; -#if 0 + switch (kdr->type) { case KDBG_CLASSTYPE : val_1 = (kdr->value1 & 0xff); @@ -790,12 +898,12 @@ kdbg_getreg(kd_regtype * kdr) } - -kdbg_readmap(kd_threadmap *buffer, size_t *number) +int +kdbg_readmap(user_addr_t buffer, size_t *number) { int avail = *number; int ret = 0; - int count = 0; + unsigned int count = 0; count = avail/sizeof (kd_threadmap); @@ -819,7 +927,7 @@ kdbg_readmap(kd_threadmap *buffer, size_t *number) if ((kdebug_flags & KDBG_MAPINIT) && kd_mapsize && kd_mapptr) { - kmem_free(kernel_map, (char *)kd_mapptr, kd_mapsize); + kmem_free(kernel_map, (vm_offset_t)kd_mapptr, kd_mapsize); kdebug_flags &= ~KDBG_MAPINIT; kd_mapsize = 0; kd_mapptr = (kd_threadmap *) 0; @@ -829,11 +937,11 @@ kdbg_readmap(kd_threadmap *buffer, size_t *number) return(ret); } -kdbg_getentropy (mach_timespec_t * buffer, size_t *number, int ms_timeout) +int +kdbg_getentropy (user_addr_t buffer, size_t *number, int ms_timeout) { int avail = *number; int ret = 0; - int count = 0; /* The number of timestamp entries that will fill buffer */ if (kd_entropy_buffer) return(EBUSY); @@ -848,11 +956,11 @@ kdbg_getentropy (mach_timespec_t * buffer, size_t *number, int ms_timeout) if (kmem_alloc(kernel_map, &kd_entropy_buftomem, (vm_size_t)kd_entropy_bufsize) == KERN_SUCCESS) { - kd_entropy_buffer = (mach_timespec_t *)kd_entropy_buftomem; + kd_entropy_buffer = (uint64_t *) kd_entropy_buftomem; } else { - kd_entropy_buffer = (mach_timespec_t *) 0; + kd_entropy_buffer = (uint64_t *) 0; kd_entropy_count = 0; kd_entropy_indx = 0; return (EINVAL); @@ -863,11 +971,13 @@ kdbg_getentropy (mach_timespec_t * buffer, size_t *number, int ms_timeout) /* Enable entropy sampling */ kdebug_enable |= KDEBUG_ENABLE_ENTROPY; + kdebug_slowcheck |= SLOW_ENTROPY; ret = tsleep (kdbg_getentropy, PRIBIO | PCATCH, "kd_entropy", (ms_timeout/(1000/HZ))); /* Disable entropy sampling */ kdebug_enable &= ~KDEBUG_ENABLE_ENTROPY; + kdebug_slowcheck &= ~SLOW_ENTROPY; *number = 0; ret = 0; @@ -885,12 +995,28 @@ kdbg_getentropy (mach_timespec_t * buffer, size_t *number, int ms_timeout) kd_entropy_count = 0; kd_entropy_indx = 0; kd_entropy_buftomem = 0; - kmem_free(kernel_map, (char *)kd_entropy_buffer, kd_entropy_bufsize); - kd_entropy_buffer = (mach_timespec_t *) 0; + kmem_free(kernel_map, (vm_offset_t)kd_entropy_buffer, kd_entropy_bufsize); + kd_entropy_buffer = (uint64_t *) 0; return(ret); } +static void +kdbg_set_nkdbufs(unsigned int value) +{ + /* + * We allow a maximum buffer size of 25% of either ram or max mapped address, whichever is smaller + * 'value' is the desired number of trace entries + */ + unsigned int max_entries = (sane_size/4) / sizeof(kd_buf); + + if (value <= max_entries) + nkdbufs = value; + else + nkdbufs = max_entries; +} + + /* * This function is provided for the CHUD toolkit only. * int val: @@ -900,12 +1026,13 @@ kdbg_getentropy (mach_timespec_t * buffer, size_t *number, int ms_timeout) * address of the enabled kdebug_chudhook function */ -void kdbg_control_chud(int val, void *fn) +void +kdbg_control_chud(int val, void *fn) { if (val) { /* enable chudhook */ - kdebug_enable |= KDEBUG_ENABLE_CHUD; kdebug_chudhook = fn; + kdebug_enable |= KDEBUG_ENABLE_CHUD; } else { /* disable chudhook */ @@ -915,84 +1042,108 @@ void kdbg_control_chud(int val, void *fn) } -kdbg_control(name, namelen, where, sizep) -int *name; -u_int namelen; -char *where; -size_t *sizep; +int +kdbg_control(int *name, __unused u_int namelen, user_addr_t where, size_t *sizep) { -int ret=0; -int size=*sizep; -int max_entries; -unsigned int value = name[1]; -kd_regtype kd_Reg; -kbufinfo_t kd_bufinfo; - -pid_t curpid; -struct proc *p, *curproc; - - if (name[0] == KERN_KDGETBUF) { - /* - Does not alter the global_state_pid - This is a passive request. - */ - if (size < sizeof(kd_bufinfo.nkdbufs)) { - /* - There is not enough room to return even - the first element of the info structure. + int ret=0; + size_t size=*sizep; + unsigned int value = name[1]; + kd_regtype kd_Reg; + kbufinfo_t kd_bufinfo; + pid_t curpid; + struct proc *p, *curproc; + + + kdbg_lock_init(); + + if ( !(kdebug_flags & KDBG_LOCKINIT)) + return(ENOMEM); + + lck_mtx_lock(kd_trace_mtx_sysctl); + + if (name[0] == KERN_KDGETBUF) { + /* + * Does not alter the global_state_pid + * This is a passive request. */ - return(EINVAL); - } - - kd_bufinfo.nkdbufs = nkdbufs; - kd_bufinfo.nkdthreads = kd_mapsize / sizeof(kd_threadmap); - kd_bufinfo.nolog = kdebug_nolog; - kd_bufinfo.flags = kdebug_flags; - kd_bufinfo.bufid = global_state_pid; + if (size < sizeof(kd_bufinfo.nkdbufs)) { + /* + * There is not enough room to return even + * the first element of the info structure. + */ + lck_mtx_unlock(kd_trace_mtx_sysctl); + + return(EINVAL); + } + kd_bufinfo.nkdbufs = nkdbufs; + kd_bufinfo.nkdthreads = kd_mapsize / sizeof(kd_threadmap); + + if ( (kdebug_slowcheck & SLOW_NOLOG) ) + kd_bufinfo.nolog = 1; + else + kd_bufinfo.nolog = 0; + kd_bufinfo.flags = kdebug_flags; + kd_bufinfo.bufid = global_state_pid; - if(size >= sizeof(kbufinfo_t)) { - /* Provide all the info we have */ - if(copyout (&kd_bufinfo, where, sizeof(kbufinfo_t))) - return(EINVAL); - } - else { - /* - For backwards compatibility, only provide - as much info as there is room for. - */ - if(copyout (&kd_bufinfo, where, size)) - return(EINVAL); - } - return(0); - } - else if (name[0] == KERN_KDGETENTROPY) { - if (kd_entropy_buffer) - return(EBUSY); - else - ret = kdbg_getentropy((mach_timespec_t *)where, sizep, value); - return (ret); - } - - if(curproc = current_proc()) - curpid = curproc->p_pid; - else - return (ESRCH); + if (size >= sizeof(kd_bufinfo)) { + /* + * Provide all the info we have + */ + if (copyout (&kd_bufinfo, where, sizeof(kd_bufinfo))) { + lck_mtx_unlock(kd_trace_mtx_sysctl); + + return(EINVAL); + } + } + else { + /* + * For backwards compatibility, only provide + * as much info as there is room for. + */ + if (copyout (&kd_bufinfo, where, size)) { + lck_mtx_unlock(kd_trace_mtx_sysctl); + + return(EINVAL); + } + } + lck_mtx_unlock(kd_trace_mtx_sysctl); + return(0); + } else if (name[0] == KERN_KDGETENTROPY) { + if (kd_entropy_buffer) + ret = EBUSY; + else + ret = kdbg_getentropy(where, sizep, value); + lck_mtx_unlock(kd_trace_mtx_sysctl); + + return (ret); + } + + if ((curproc = current_proc()) != NULL) + curpid = curproc->p_pid; + else { + lck_mtx_unlock(kd_trace_mtx_sysctl); + + return (ESRCH); + } if (global_state_pid == -1) global_state_pid = curpid; - else if (global_state_pid != curpid) - { - if((p = pfind(global_state_pid)) == NULL) - { - /* The global pid no longer exists */ - global_state_pid = curpid; - } - else - { - /* The global pid exists, deny this request */ + else if (global_state_pid != curpid) { + if ((p = proc_find(global_state_pid)) == NULL) { + /* + * The global pid no longer exists + */ + global_state_pid = curpid; + } else { + /* + * The global pid exists, deny this request + */ + proc_rele(p); + lck_mtx_unlock(kd_trace_mtx_sysctl); + return(EBUSY); - } - } + } + } switch(name[0]) { case KERN_KDEFLAGS: @@ -1012,26 +1163,19 @@ struct proc *p, *curproc; ret=EINVAL; break; } - } + kdbg_mapinit(); - if (value) - kdebug_enable |= KDEBUG_ENABLE_TRACE; + kdebug_enable |= KDEBUG_ENABLE_TRACE; + kdebug_slowcheck &= ~SLOW_NOLOG; + } else - kdebug_enable &= ~KDEBUG_ENABLE_TRACE; - - kdebug_nolog = (value)?0:1; - - if (kdebug_enable & KDEBUG_ENABLE_TRACE) - kdbg_mapinit(); + { + kdebug_enable &= ~KDEBUG_ENABLE_TRACE; + kdebug_slowcheck |= SLOW_NOLOG; + } break; case KERN_KDSETBUF: - /* We allow a maximum buffer size of 25% of memory */ - /* 'value' is the desired number of trace entries */ - max_entries = (mem_size/4) / sizeof(kd_buf); - if (value <= max_entries) - nkdbufs = value; - else - nkdbufs = max_entries; + kdbg_set_nkdbufs(value); break; case KERN_KDSETUP: ret=kdbg_reinit(); @@ -1086,7 +1230,7 @@ struct proc *p, *curproc; ret = kdbg_setpidex(&kd_Reg); break; case KERN_KDTHRMAP: - ret = kdbg_readmap((kd_threadmap *)where, sizep); + ret = kdbg_readmap(where, sizep); break; case KERN_KDSETRTCDEC: if (size < sizeof(kd_regtype)) { @@ -1103,104 +1247,329 @@ struct proc *p, *curproc; default: ret= EINVAL; } + lck_mtx_unlock(kd_trace_mtx_sysctl); + return(ret); } -kdbg_read(kd_buf * buffer, size_t *number) -{ -int avail=*number; -int count=0; -int copycount=0; -int totalcount=0; -int s; -unsigned int my_kdebug_flags; -kd_buf * my_kd_bufptr; - s = ml_set_interrupts_enabled(FALSE); - usimple_lock(&kd_trace_lock); - my_kdebug_flags = kdebug_flags; - my_kd_bufptr = kd_bufptr; - usimple_unlock(&kd_trace_lock); - ml_set_interrupts_enabled(s); +/* + * This code can run concurrently with kernel_debug_internal() + * without the need of any locks, because all reads of kd_bufptr[i], + * which get modified by kernel_debug_internal(), are safe. + */ +int +kdbg_read(user_addr_t buffer, size_t *number) +{ + unsigned int count; + unsigned int cpu; + int mincpu; + uint64_t mintime, t, last_wrap_time; + int last_wrap_cpu; + int error = 0; + kd_buf *tempbuf; + uint32_t tempbuf_count; + uint32_t tempbuf_number; + unsigned int old_kdebug_flags, new_kdebug_flags; + unsigned int old_kdebug_slowcheck, new_kdebug_slowcheck; + boolean_t first_event = TRUE; + + count = *number/sizeof(kd_buf); + *number = 0; + + if (count == 0 || !(kdebug_flags & KDBG_BUFINIT) || kdcopybuf == 0) + return EINVAL; - count = avail/sizeof(kd_buf); - if (count) { - if ((my_kdebug_flags & KDBG_BUFINIT) && kd_bufsize && kd_buffer) { - if (count > nkdbufs) - count = nkdbufs; - if (!(my_kdebug_flags & KDBG_WRAPPED) && (my_kd_bufptr > kd_readlast)) - { - copycount = my_kd_bufptr-kd_readlast; - if (copycount > count) - copycount = count; - - if (copyout(kd_readlast, buffer, copycount * sizeof(kd_buf))) - { - *number = 0; - return(EINVAL); - } - kd_readlast += copycount; - *number = copycount; - return(0); - } - else if (!(my_kdebug_flags & KDBG_WRAPPED) && (my_kd_bufptr == kd_readlast)) - { - *number = 0; - return(0); - } + /* + * because we hold kd_trace_mtx_sysctl, no other control threads can + * be playing with kdebug_flags... the code that cuts new events could + * be running, but it only reads kdebug_flags, it doesn't write it.. + * use an OSCompareAndSwap to make sure the other processors see the + * change of state immediately, not to protect against 2 threads racing to update it + */ + old_kdebug_slowcheck = kdebug_slowcheck; + do { + old_kdebug_flags = kdebug_flags; + new_kdebug_flags = old_kdebug_flags & ~KDBG_WRAPPED; + new_kdebug_flags |= KDBG_NOWRAP; + } while ( !OSCompareAndSwap((UInt32)old_kdebug_flags, (UInt32)new_kdebug_flags, (UInt32 *)&kdebug_flags)); + + last_wrap_time = 0; + last_wrap_cpu = -1; + + for (cpu = 0; cpu < kd_cpus; cpu++) { + kd_buf *cur_bufptr; + + if ((cur_bufptr = kdbip[cpu].kd_bufptr) >= kdbip[cpu].kd_buflast) + cur_bufptr = kdbip[cpu].kd_buffer; + + if (kdbip[cpu].kd_wrapped) { + kdbip[cpu].kd_wrapped = 0; + kdbip[cpu].kd_readlast = cur_bufptr; + kdbip[cpu].kd_stop = cur_bufptr; + + if (kd_cpus > 1 && ((cur_bufptr->timestamp & KDBG_TIMESTAMP_MASK) > last_wrap_time)) { + last_wrap_time = cur_bufptr->timestamp & KDBG_TIMESTAMP_MASK; + last_wrap_cpu = cpu; + } + } else { + if (kdbip[cpu].kd_readlast == cur_bufptr) + kdbip[cpu].kd_stop = 0; else - { - if (my_kdebug_flags & KDBG_WRAPPED) - { - kd_readlast = my_kd_bufptr; - kdebug_flags &= ~KDBG_WRAPPED; - } - - /* Note that by setting kd_readlast equal to my_kd_bufptr, - we now treat the kd_buffer read the same as if we weren't - wrapped and my_kd_bufptr was less than kd_readlast. - */ - - /* first copyout from readlast to end of kd_buffer */ - copycount = kd_buflast - kd_readlast; - if (copycount > count) - copycount = count; - if (copyout(kd_readlast, buffer, copycount * sizeof(kd_buf))) - { - *number = 0; - return(EINVAL); - } - buffer += copycount; - count -= copycount; - totalcount = copycount; - kd_readlast += copycount; - if (kd_readlast == kd_buflast) - kd_readlast = kd_buffer; - if (count == 0) - { - *number = totalcount; - return(0); - } - - /* second copyout from top of kd_buffer to bufptr */ - copycount = my_kd_bufptr - kd_readlast; - if (copycount > count) - copycount = count; - if (copycount == 0) - { - *number = totalcount; - return(0); - } - if (copyout(kd_readlast, buffer, copycount * sizeof(kd_buf))) - { - return(EINVAL); - } - kd_readlast += copycount; - totalcount += copycount; - *number = totalcount; - return(0); - } - } /* end if KDBG_BUFINIT */ - } /* end if count */ - return (EINVAL); + kdbip[cpu].kd_stop = cur_bufptr; + } + } + if (count > nkdbufs) + count = nkdbufs; + + if ((tempbuf_count = count) > KDCOPYBUF_COUNT) + tempbuf_count = KDCOPYBUF_COUNT; + + if (last_wrap_cpu == -1) + first_event = FALSE; + + while (count) { + tempbuf = kdcopybuf; + tempbuf_number = 0; + + while (tempbuf_count) { + mintime = 0xffffffffffffffffULL; /* all actual timestamps are below */ + mincpu = -1; + + for (cpu = 0; cpu < kd_cpus; cpu++) { + if (kdbip[cpu].kd_stop == 0) /* empty buffer */ + continue; + t = kdbip[cpu].kd_readlast[0].timestamp & KDBG_TIMESTAMP_MASK; + + if (t < mintime) { + mintime = t; + mincpu = cpu; + } + } + if (mincpu < 0) + /* + * all buffers ran empty early + */ + break; + + if (first_event == TRUE) { + /* + * make sure we leave room for the + * LAST_WRAPPER event we inject + * by throwing away the first event + * it's better to lose that one + * than the last one + */ + first_event = FALSE; + + kdbip[mincpu].kd_readlast++; + + if (kdbip[mincpu].kd_readlast == kdbip[mincpu].kd_buflast) + kdbip[mincpu].kd_readlast = kdbip[mincpu].kd_buffer; + if (kdbip[mincpu].kd_readlast == kdbip[mincpu].kd_stop) + kdbip[mincpu].kd_stop = 0; + + continue; + } + if (last_wrap_cpu == mincpu) { + tempbuf->debugid = MISCDBG_CODE(DBG_BUFFER, 0) | DBG_FUNC_NONE; + tempbuf->arg1 = kd_bufsize / sizeof(kd_buf); + tempbuf->arg2 = kd_cpus; + tempbuf->arg3 = 0; + tempbuf->arg4 = 0; + tempbuf->arg5 = (int)current_thread(); + + tempbuf->timestamp = last_wrap_time | (((uint64_t)last_wrap_cpu) << KDBG_CPU_SHIFT); + + tempbuf++; + + last_wrap_cpu = -1; + + } else { + *(tempbuf++) = kdbip[mincpu].kd_readlast[0]; + + kdbip[mincpu].kd_readlast++; + + if (kdbip[mincpu].kd_readlast == kdbip[mincpu].kd_buflast) + kdbip[mincpu].kd_readlast = kdbip[mincpu].kd_buffer; + if (kdbip[mincpu].kd_readlast == kdbip[mincpu].kd_stop) + kdbip[mincpu].kd_stop = 0; + } + tempbuf_count--; + tempbuf_number++; + } + if (tempbuf_number) { + if ((error = copyout(kdcopybuf, buffer, tempbuf_number * sizeof(kd_buf)))) { + *number = 0; + error = EINVAL; + break; + } + count -= tempbuf_number; + *number += tempbuf_number; + buffer += (tempbuf_number * sizeof(kd_buf)); + } + if (tempbuf_count) + /* + * all trace buffers are empty + */ + break; + + if ((tempbuf_count = count) > KDCOPYBUF_COUNT) + tempbuf_count = KDCOPYBUF_COUNT; + } + if ( !(old_kdebug_flags & KDBG_NOWRAP)) { + do { + old_kdebug_flags = kdebug_flags; + new_kdebug_flags = old_kdebug_flags & ~KDBG_NOWRAP; + } while ( !OSCompareAndSwap((UInt32)old_kdebug_flags, (UInt32)new_kdebug_flags, (UInt32 *)&kdebug_flags)); + + if ( !(old_kdebug_slowcheck & SLOW_NOLOG)) { + do { + old_kdebug_slowcheck = kdebug_slowcheck; + new_kdebug_slowcheck = old_kdebug_slowcheck & ~SLOW_NOLOG; + } while ( !OSCompareAndSwap((UInt32)old_kdebug_slowcheck, (UInt32)new_kdebug_slowcheck, (UInt32 *)&kdebug_slowcheck)); + } + } + return (error); +} + + +unsigned char *getProcName(struct proc *proc); +unsigned char *getProcName(struct proc *proc) { + + return (unsigned char *) &proc->p_comm; /* Return pointer to the proc name */ + +} + +#define STACKSHOT_SUBSYS_LOCK() lck_mtx_lock(&stackshot_subsys_mutex) +#define STACKSHOT_SUBSYS_UNLOCK() lck_mtx_unlock(&stackshot_subsys_mutex) +#ifdef __i386__ +#define TRAP_DEBUGGER __asm__ volatile("int3"); +#endif +#ifdef __ppc__ +#define TRAP_DEBUGGER __asm__ volatile("tw 4,r3,r3"); +#endif + +#define SANE_TRACEBUF_SIZE 2*1024*1024 + +/* Initialize the mutex governing access to the stack snapshot subsystem */ +__private_extern__ void +stackshot_lock_init( void ) +{ + stackshot_subsys_lck_grp_attr = lck_grp_attr_alloc_init(); + + stackshot_subsys_lck_grp = lck_grp_alloc_init("stackshot_subsys_lock", stackshot_subsys_lck_grp_attr); + + stackshot_subsys_lck_attr = lck_attr_alloc_init(); + + lck_mtx_init(&stackshot_subsys_mutex, stackshot_subsys_lck_grp, stackshot_subsys_lck_attr); +} + +/* + * stack_snapshot: Obtains a coherent set of stack traces for all threads + * on the system, tracing both kernel and user stacks + * where available. Uses machine specific trace routines + * for ppc, ppc64 and x86. + * Inputs: uap->pid - process id of process to be traced, or -1 + * for the entire system + * uap->tracebuf - address of the user space destination + * buffer + * uap->tracebuf_size - size of the user space trace buffer + * uap->options - various options, including the maximum + * number of frames to trace. + * Outputs: EPERM if the caller is not privileged + * EINVAL if the supplied trace buffer isn't sanely sized + * ENOMEM if we don't have enough memory to satisfy the + * request + * ENOENT if the target pid isn't found + * ENOSPC if the supplied buffer is insufficient + * *retval contains the number of bytes traced, if successful + * and -1 otherwise. If the request failed due to + * tracebuffer exhaustion, we copyout as much as possible. + */ +int +stack_snapshot(struct proc *p, register struct stack_snapshot_args *uap, register_t *retval) { + int error = 0; + + if ((error = suser(kauth_cred_get(), &p->p_acflag))) + return(error); + + return stack_snapshot2(uap->pid, uap->tracebuf, uap->tracebuf_size, + uap->options, retval); +} + +int +stack_snapshot2(pid_t pid, user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t options, register_t *retval) +{ + int error = 0; + unsigned bytesTraced = 0; + + *retval = -1; +/* Serialize tracing */ + STACKSHOT_SUBSYS_LOCK(); + + if ((tracebuf_size <= 0) || (tracebuf_size > SANE_TRACEBUF_SIZE)) { + error = EINVAL; + goto error_exit; + } + + MALLOC(stackshot_snapbuf, void *, tracebuf_size, M_TEMP, M_WAITOK); + + if (stackshot_snapbuf == NULL) { + error = ENOMEM; + goto error_exit; + } +/* Preload trace parameters*/ + kdp_snapshot_preflight(pid, stackshot_snapbuf, tracebuf_size, options); + +/* Trap to the debugger to obtain a coherent stack snapshot; this populates + * the trace buffer + */ + if (panic_active()) { + error = ENOMEM; + goto error_exit; + } + + TRAP_DEBUGGER; + + bytesTraced = kdp_stack_snapshot_bytes_traced(); + + if (bytesTraced > 0) { + if ((error = copyout(stackshot_snapbuf, tracebuf, + ((bytesTraced < tracebuf_size) ? + bytesTraced : tracebuf_size)))) + goto error_exit; + *retval = bytesTraced; + } + else { + error = ENOENT; + goto error_exit; + } + + error = kdp_stack_snapshot_geterror(); + if (error == -1) { + error = ENOSPC; + *retval = -1; + goto error_exit; + } + +error_exit: + if (stackshot_snapbuf != NULL) + FREE(stackshot_snapbuf, M_TEMP); + stackshot_snapbuf = NULL; + STACKSHOT_SUBSYS_UNLOCK(); + return error; +} + +void +start_kern_tracing(unsigned int new_nkdbufs) { + if (!new_nkdbufs) + return; + kdbg_set_nkdbufs(new_nkdbufs); + kdbg_lock_init(); + kdbg_reinit(); + kdebug_enable |= KDEBUG_ENABLE_TRACE; + kdebug_slowcheck &= ~SLOW_NOLOG; + kdbg_mapinit(); + printf("kernel tracing started\n"); }