]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_pcsamples.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / bsd / kern / kern_pcsamples.c
CommitLineData
1c79356b 1/*
5d5c5d0d
A
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
6601e61a 4 * @APPLE_LICENSE_HEADER_START@
1c79356b 5 *
6601e61a
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
8f6c56a5 11 *
6601e61a
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
6601e61a
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
8f6c56a5 19 *
6601e61a 20 * @APPLE_LICENSE_HEADER_END@
1c79356b
A
21 */
22
23#include <sys/kdebug.h>
24#include <sys/errno.h>
25#include <sys/param.h>
91447636 26#include <sys/proc_internal.h>
1c79356b
A
27#include <sys/vm.h>
28#include <sys/sysctl.h>
91447636 29#include <sys/systm.h>
1c79356b 30#include <vm/vm_kern.h>
91447636 31#include <machine/machine_routines.h>
1c79356b 32
91447636
A
33vm_offset_t pc_buftomem = 0;
34unsigned int * pc_buffer = 0; /* buffer that holds each pc */
35unsigned int * pc_bufptr = 0;
36unsigned int * pc_buflast = 0;
1c79356b
A
37unsigned int npcbufs = 8192; /* number of pc entries in buffer */
38unsigned int pc_bufsize = 0;
39unsigned int pcsample_flags = 0;
40unsigned int pcsample_enable = 0;
41
0b4e3aa0
A
42pid_t pc_sample_pid = 0;
43boolean_t pc_trace_frameworks = FALSE;
44
1c79356b
A
45char pcsample_comm[MAXCOMLEN + 1];
46
47/* Set the default framework boundaries */
91447636
A
48unsigned int pcsample_beg = 0;
49unsigned int pcsample_end = 0;
1c79356b
A
50
51static pid_t global_state_pid = -1; /* Used to control exclusive use of pc_buffer */
52
53extern int pc_trace_buf[];
54extern int pc_trace_cnt;
55
91447636
A
56void add_pcbuffer(void);
57int branch_tracing_enabled(void);
58int disable_branch_tracing(void);
59int enable_branch_tracing(void);
60int pcsamples_bootstrap(void);
61void pcsamples_clear(void);
62int pcsamples_control(int *name, u_int namelen, user_addr_t where, size_t *sizep);
63int pcsamples_read(user_addr_t buffer, size_t *number);
64int pcsamples_reinit(void);
65
0b4e3aa0 66int
91447636 67enable_branch_tracing(void)
0b4e3aa0
A
68{
69#ifndef i386
70 struct proc *p;
71 if (-1 != pc_sample_pid) {
72 p = pfind(pc_sample_pid);
73 if (p) {
74 p->p_flag |= P_BTRACE;
75 }
76 }
77 else {
78 pc_trace_frameworks = TRUE;
79 }
80
81 return 1;
82
83#else
84 return 0;
85#endif
86}
87
88int
91447636 89disable_branch_tracing(void)
0b4e3aa0 90{
91447636
A
91 struct proc *p;
92 switch (pc_sample_pid) {
0b4e3aa0 93 case -1:
91447636
A
94 pc_trace_frameworks = FALSE;
95 break;
96 case 0:
97 break;
98 default:
99 p = pfind(pc_sample_pid);
100 if (p) {
101 p->p_flag &= ~P_BTRACE;
102 }
103 break;
104 }
105 clr_be_bit();
106 return 1;
0b4e3aa0
A
107}
108
109/*
110 * this only works for the current proc as it
111 * is called from context_switch in the scheduler
112 */
113int
91447636 114branch_tracing_enabled(void)
0b4e3aa0
A
115{
116 struct proc *p = current_proc();
117 if (TRUE == pc_trace_frameworks) return TRUE;
118 if (p) {
119 return (P_BTRACE == (p->p_flag & P_BTRACE));
120 }
121 return 0;
122}
123
124
1c79356b 125void
91447636 126add_pcbuffer(void)
1c79356b
A
127{
128 int i;
91447636 129 unsigned int pc;
1c79356b
A
130
131 if (!pcsample_enable)
132 return;
133
1c79356b
A
134 for (i=0; i < pc_trace_cnt; i++)
135 {
136 pc = pc_trace_buf[i];
137
138 if ((pcsample_beg <= pc) && (pc < pcsample_end))
139 {
140 if (pc_bufptr > pc_buffer)
141 {
142 if ( (*(pc_bufptr-1)) == pc )
143 continue; /* Ignore, probably spinning */
144 }
145
146 /* Then the sample is in our range */
91447636 147 *pc_bufptr = pc;
1c79356b
A
148 pc_bufptr++;
149 }
150 }
151
152 /* We never wrap the buffer */
153 if ((pc_bufptr + pc_trace_cnt) >= pc_buflast)
154 {
155 pcsample_enable = 0;
0b4e3aa0 156 (void)disable_branch_tracing();
1c79356b
A
157 wakeup(&pcsample_enable);
158 }
159 return;
160}
161
91447636
A
162int
163pcsamples_bootstrap(void)
1c79356b 164{
0b4e3aa0 165 if (!disable_branch_tracing())
1c79356b
A
166 return(ENOTSUP);
167
168 pc_bufsize = npcbufs * sizeof(* pc_buffer);
169 if (kmem_alloc(kernel_map, &pc_buftomem,
170 (vm_size_t)pc_bufsize) == KERN_SUCCESS)
91447636 171 pc_buffer = (unsigned int *) pc_buftomem;
1c79356b 172 else
91447636 173 pc_buffer = NULL;
1c79356b
A
174
175 if (pc_buffer) {
176 pc_bufptr = pc_buffer;
177 pc_buflast = &pc_bufptr[npcbufs];
178 pcsample_enable = 0;
179 return(0);
180 } else {
181 pc_bufsize=0;
182 return(EINVAL);
183 }
184
185}
186
91447636
A
187int
188pcsamples_reinit(void)
1c79356b 189{
91447636 190 int ret=0;
1c79356b 191
91447636 192 pcsample_enable = 0;
1c79356b
A
193
194 if (pc_bufsize && pc_buffer)
55e303ae 195 kmem_free(kernel_map, (vm_offset_t)pc_buffer, pc_bufsize);
1c79356b
A
196
197 ret= pcsamples_bootstrap();
198 return(ret);
199}
200
91447636
A
201void
202pcsamples_clear(void)
1c79356b 203{
91447636
A
204 /* Clean up the sample buffer, set defaults */
205 global_state_pid = -1;
1c79356b
A
206 pcsample_enable = 0;
207 if(pc_bufsize && pc_buffer)
55e303ae 208 kmem_free(kernel_map, (vm_offset_t)pc_buffer, pc_bufsize);
91447636
A
209 pc_buffer = NULL;
210 pc_bufptr = NULL;
211 pc_buflast = NULL;
1c79356b
A
212 pc_bufsize = 0;
213 pcsample_beg= 0;
214 pcsample_end= 0;
215 bzero((void *)pcsample_comm, sizeof(pcsample_comm));
0b4e3aa0
A
216 (void)disable_branch_tracing();
217 pc_sample_pid = 0;
218 pc_trace_frameworks = FALSE;
1c79356b
A
219}
220
91447636
A
221int
222pcsamples_control(int *name, __unused u_int namelen, user_addr_t where, size_t *sizep)
1c79356b 223{
91447636
A
224 int ret=0;
225 size_t size=*sizep;
226 int value = name[1];
227 pcinfo_t pc_bufinfo;
228 pid_t *pidcheck;
229
230 pid_t curpid;
231 struct proc *p, *curproc;
232
233 if (name[0] != PCSAMPLE_GETNUMBUF)
234 {
235 curproc = current_proc();
236 if (curproc)
1c79356b
A
237 curpid = curproc->p_pid;
238 else
239 return (ESRCH);
240
241 if (global_state_pid == -1)
242 global_state_pid = curpid;
243 else if (global_state_pid != curpid)
244 {
245 if((p = pfind(global_state_pid)) == NULL)
246 {
247 /* The global pid no longer exists */
248 global_state_pid = curpid;
249 }
250 else
251 {
252 /* The global pid exists, deny this request */
253 return(EBUSY);
254 }
91447636
A
255 }
256 }
1c79356b
A
257
258
259 switch(name[0]) {
91447636 260 case PCSAMPLE_DISABLE: /* used to disable */
1c79356b
A
261 pcsample_enable=0;
262 break;
91447636
A
263 case PCSAMPLE_SETNUMBUF:
264 /* The buffer size is bounded by a min and max number of samples */
265 if (value < pc_trace_cnt) {
266 ret=EINVAL;
1c79356b
A
267 break;
268 }
269 if (value <= MAX_PCSAMPLES)
91447636
A
270 /* npcbufs = value & ~(PC_TRACE_CNT-1); */
271 npcbufs = value;
1c79356b 272 else
91447636 273 npcbufs = MAX_PCSAMPLES;
1c79356b 274 break;
91447636
A
275 case PCSAMPLE_GETNUMBUF:
276 if (size < sizeof(pc_bufinfo)) {
277 ret=EINVAL;
1c79356b
A
278 break;
279 }
280 pc_bufinfo.npcbufs = npcbufs;
281 pc_bufinfo.bufsize = pc_bufsize;
282 pc_bufinfo.enable = pcsample_enable;
283 pc_bufinfo.pcsample_beg = pcsample_beg;
284 pc_bufinfo.pcsample_end = pcsample_end;
285 if(copyout (&pc_bufinfo, where, sizeof(pc_bufinfo)))
286 {
287 ret=EINVAL;
288 }
289 break;
91447636 290 case PCSAMPLE_SETUP:
1c79356b
A
291 ret=pcsamples_reinit();
292 break;
91447636 293 case PCSAMPLE_REMOVE:
1c79356b
A
294 pcsamples_clear();
295 break;
91447636 296 case PCSAMPLE_READBUF:
1c79356b
A
297 /* A nonzero value says enable and wait on the buffer */
298 /* A zero value says read up the buffer immediately */
299 if (value == 0)
300 {
301 /* Do not wait on the buffer */
302 pcsample_enable = 0;
0b4e3aa0 303 (void)disable_branch_tracing();
1c79356b
A
304 ret = pcsamples_read(where, sizep);
305 break;
306 }
307 else if ((pc_bufsize <= 0) || (!pc_buffer))
308 {
309 /* enable only if buffer is initialized */
310 ret=EINVAL;
311 break;
312 }
313
314 /* Turn on branch tracing */
0b4e3aa0 315 if (!enable_branch_tracing())
1c79356b 316 {
0b4e3aa0 317 ret = ENOTSUP;
1c79356b
A
318 break;
319 }
320
321 /* Enable sampling */
322 pcsample_enable = 1;
323
324 ret = tsleep(&pcsample_enable, PRIBIO | PCATCH, "pcsample", 0);
325 pcsample_enable = 0;
0b4e3aa0 326 (void)disable_branch_tracing();
1c79356b
A
327
328 if (ret)
329 {
330 /* Eventually fix this... if (ret != EINTR) */
331 if (ret)
332 {
333 /* On errors, except EINTR, we want to cleanup buffer ptrs */
334 /* pc_bufptr = pc_buffer; */
335 *sizep = 0;
336 }
337 }
338 else
339 {
340 /* The only way to get here is if the buffer is full */
341 ret = pcsamples_read(where, sizep);
342 }
343
344 break;
91447636
A
345 case PCSAMPLE_SETREG:
346 if (size < sizeof(pc_bufinfo))
1c79356b
A
347 {
348 ret = EINVAL;
349 break;
350 }
91447636 351 if (copyin(where, &pc_bufinfo, sizeof(pc_bufinfo)))
1c79356b
A
352 {
353 ret = EINVAL;
354 break;
355 }
356
357 pcsample_beg = pc_bufinfo.pcsample_beg;
358 pcsample_end = pc_bufinfo.pcsample_end;
359 break;
91447636
A
360 case PCSAMPLE_COMM:
361 if (!(sizeof(pcsample_comm) > size))
362 {
363 ret = EINVAL;
364 break;
365 }
366 bzero((void *)pcsample_comm, sizeof(pcsample_comm));
367 if (copyin(where, pcsample_comm, size))
368 {
369 ret = EINVAL;
0b4e3aa0 370 break;
91447636 371 }
0b4e3aa0
A
372
373 /* Check for command name or pid */
91447636
A
374 if (pcsample_comm[0] != '\0')
375 {
376 ret= ENOTSUP;
0b4e3aa0 377 break;
91447636 378 }
0b4e3aa0
A
379 else
380 {
381 if (size != (2 * sizeof(pid_t)))
382 {
383 ret = EINVAL;
384 break;
385 }
386 else
387 {
388 pidcheck = (pid_t *)pcsample_comm;
389 pc_sample_pid = pidcheck[1];
390 }
391 }
1c79356b 392 break;
91447636
A
393 default:
394 ret= ENOTSUP;
1c79356b
A
395 break;
396 }
397 return(ret);
398}
399
400
401/*
402 This buffer must be read up in one call.
403 If the buffer isn't big enough to hold
404 all the samples, it will copy up enough
405 to fill the buffer and throw the rest away.
406 This buffer never wraps.
407*/
91447636
A
408int
409pcsamples_read(user_addr_t buffer, size_t *number)
1c79356b 410{
91447636
A
411 size_t count=0;
412 size_t copycount;
1c79356b 413
91447636 414 count = (*number)/sizeof(* pc_buffer);
1c79356b
A
415
416 if (count && pc_bufsize && pc_buffer)
417 {
418 copycount = pc_bufptr - pc_buffer;
419
420 if (copycount <= 0)
421 {
422 *number = 0;
423 return(0);
424 }
425
426 if (copycount > count)
427 copycount = count;
428
429 /* We actually have data to send up */
91447636 430 if(copyout(pc_buffer, buffer, copycount * sizeof(* pc_buffer)))
1c79356b
A
431 {
432 *number = 0;
433 return(EINVAL);
434 }
435 *number = copycount;
436 pc_bufptr = pc_buffer;
437 return(0);
438 }
439 else
440 {
441 *number = 0;
442 return(0);
443 }
444}
445