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