1/* 2 * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28#include <mach/mach_types.h> 29#include <kern/thread.h> 30#include <kern/machine.h> 31#include <kern/kalloc.h> 32#include <sys/errno.h> 33 34#include <kperf/sample.h> 35#include <kperf/pet.h> 36#include <kperf/action.h> 37#include <kperf/kperf.h> 38#include <kperf/timetrigger.h> 39 40#include <kern/ipc_tt.h> /* port_name_to_task */ 41 42/** misc functions **/ 43#include <chud/chud_xnu.h> /* XXX: should bust this out */ 44 45/* thread on CPUs before starting the PET thread */ 46thread_t *kperf_thread_on_cpus = NULL; 47 48/* interupt sample buffers -- one wired per CPU */ 49static struct kperf_sample *intr_samplev = NULL; 50static unsigned intr_samplec = 0; 51 52/* track recursion in the trace code */ 53static struct 54{ 55 int active; 56 int pad[64 / sizeof(int)]; 57} *kpdbg_recursev; 58static unsigned kpdbg_recursec = 0; 59 60/* Curren sampling status */ 61static unsigned sampling_status = KPERF_SAMPLING_OFF; 62 63/* Make sure we only init once */ 64static unsigned kperf_initted = 0; 65 66extern void (*chudxnu_thread_ast_handler)(thread_t); 67 68struct kperf_sample* 69kperf_intr_sample_buffer(void) 70{ 71 unsigned ncpu = chudxnu_cpu_number(); 72 73 // XXX: assert? 74 if( ncpu >= intr_samplec ) 75 return NULL; 76 77 return &intr_samplev[ncpu]; 78} 79 80int 81kperf_kdbg_recurse(int step) 82{ 83 unsigned ncpu = chudxnu_cpu_number(); 84 85 // XXX: assert? 86 if( ncpu >= kpdbg_recursec ) 87 return 1; 88 89 /* recursing in, available */ 90 if( (step > 0) 91 && (kpdbg_recursev[ncpu].active == 0) ) 92 { 93 kpdbg_recursev[ncpu].active = 1; 94 return 0; 95 } 96 97 /* recursing in, unavailable */ 98 if( (step > 0) 99 && (kpdbg_recursev[ncpu].active != 0) ) 100 { 101 return 1; 102 } 103 104 /* recursing out, unavailable */ 105 if( (step < 0) 106 && (kpdbg_recursev[ncpu].active != 0) ) 107 { 108 kpdbg_recursev[ncpu].active = 0; 109 return 0; 110 } 111 112 /* recursing out, available */ 113 if( (step < 0) 114 && (kpdbg_recursev[ncpu].active == 0) ) 115 panic( "return from non-recursed kperf kdebug call" ); 116 117 panic( "unknown kperf kdebug call" ); 118 return 1; 119} 120 121/* setup interrupt sample buffers */ 122int 123kperf_init(void) 124{ 125 unsigned ncpus = 0; 126 int err; 127 128 if( kperf_initted ) 129 return 0; 130 131 /* get number of cpus */ 132 ncpus = machine_info.logical_cpu_max; 133 134 kperf_thread_on_cpus = kalloc( ncpus * sizeof(*kperf_thread_on_cpus) ); 135 if( kperf_thread_on_cpus == NULL ) 136 { 137 err = ENOMEM; 138 goto error; 139 } 140 141 /* clear it */ 142 bzero( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) ); 143 144 /* make the CPU array 145 * FIXME: cache alignment 146 */ 147 intr_samplev = kalloc( ncpus * sizeof(*intr_samplev)); 148 intr_samplec = ncpus; 149 150 if( intr_samplev == NULL ) 151 { 152 err = ENOMEM; 153 goto error; 154 } 155 156 /* clear it */ 157 bzero( intr_samplev, ncpus * sizeof(*intr_samplev) ); 158 159 /* make the recursion array */ 160 kpdbg_recursev = kalloc( ncpus * sizeof(*kpdbg_recursev)); 161 kpdbg_recursec = ncpus; 162 163 /* clear it */ 164 bzero( kpdbg_recursev, ncpus * sizeof(*kpdbg_recursev) ); 165 166 /* we're done */ 167 kperf_initted = 1; 168 169 return 0; 170error: 171 if( intr_samplev ) 172 kfree( intr_samplev, ncpus * sizeof(*intr_samplev) ); 173 if( kperf_thread_on_cpus ) 174 kfree( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) ); 175 return err; 176} 177 178/* random misc-ish functions */ 179uint32_t 180kperf_get_thread_bits( thread_t thread ) 181{ 182 return thread->t_chud; 183} 184 185void 186kperf_set_thread_bits( thread_t thread, uint32_t bits ) 187{ 188 thread->t_chud = bits; 189} 190 191/* mark an AST to fire on a thread */ 192void 193kperf_set_thread_ast( thread_t thread ) 194{ 195 /* FIXME: only call this on current thread from an interrupt 196 * handler for now... 197 */ 198 if( thread != current_thread() ) 199 panic( "unsafe AST set" ); 200 201 act_set_kperf(thread); 202} 203 204unsigned 205kperf_sampling_status(void) 206{ 207 return sampling_status; 208} 209 210int 211kperf_sampling_enable(void) 212{ 213 /* already running! */ 214 if( sampling_status == KPERF_SAMPLING_ON ) 215 return 0; 216 217 if ( sampling_status != KPERF_SAMPLING_OFF ) 218 panic( "kperf: sampling wasn't off" ); 219 220 /* make sure interrupt tables and actions are initted */ 221 if( !kperf_initted 222 || (kperf_action_get_count() == 0) ) 223 return ECANCELED; 224 225 /* mark as running */ 226 sampling_status = KPERF_SAMPLING_ON; 227 228 /* tell timers to enable */ 229 kperf_timer_go(); 230 231 return 0; 232} 233 234int 235kperf_sampling_disable(void) 236{ 237 if( sampling_status != KPERF_SAMPLING_ON ) 238 return 0; 239 240 /* mark a shutting down */ 241 sampling_status = KPERF_SAMPLING_SHUTDOWN; 242 243 /* tell timers to disable */ 244 kperf_timer_stop(); 245 246 /* mark as off */ 247 sampling_status = KPERF_SAMPLING_OFF; 248 249 return 0; 250} 251 252int 253kperf_port_to_pid(mach_port_name_t portname) 254{ 255 task_t task; 256 int pid; 257 258 if( !MACH_PORT_VALID(portname) ) 259 return -1; 260 261 task = port_name_to_task(portname); 262 263 if( task == TASK_NULL ) 264 return -1; 265 266 267 pid = chudxnu_pid_for_task(task); 268 269 task_deallocate_internal(task); 270 271 return pid; 272} 273