2 * Copyright (c) 2000-2012 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/kdebug.h>
30 #include <sys/errno.h>
31 #include <sys/param.h>
32 #include <sys/proc_internal.h>
34 #include <sys/sysctl.h>
35 #include <sys/systm.h>
36 #include <vm/vm_kern.h>
37 #include <machine/machine_routines.h>
39 vm_offset_t pc_buftomem
= 0;
40 unsigned int * pc_buffer
= 0; /* buffer that holds each pc */
41 unsigned int * pc_bufptr
= 0;
42 unsigned int * pc_buflast
= 0;
43 unsigned int npcbufs
= 8192; /* number of pc entries in buffer */
44 unsigned int pc_bufsize
= 0;
45 unsigned int pcsample_flags
= 0;
46 unsigned int pcsample_enable
= 0;
48 pid_t pc_sample_pid
= 0;
49 boolean_t pc_trace_frameworks
= FALSE
;
51 char pcsample_comm
[MAXCOMLEN
+ 1];
53 /* Set the default framework boundaries */
54 unsigned int pcsample_beg
= 0;
55 unsigned int pcsample_end
= 0;
57 static pid_t global_state_pid
= -1; /* Used to control exclusive use of pc_buffer */
59 extern unsigned int pc_trace_buf
[];
60 extern int pc_trace_cnt
;
62 void add_pcbuffer(void);
63 int branch_tracing_enabled(void);
64 int disable_branch_tracing(void);
65 int enable_branch_tracing(void);
66 int pcsamples_bootstrap(void);
67 void pcsamples_clear(void);
68 int pcsamples_control(int *name
, u_int namelen
, user_addr_t where
, size_t *sizep
);
69 int pcsamples_read(user_addr_t buffer
, size_t *number
);
70 int pcsamples_reinit(void);
73 enable_branch_tracing(void)
76 if (-1 != pc_sample_pid
) {
77 p
= proc_find(pc_sample_pid
);
83 pc_trace_frameworks
= TRUE
;
90 disable_branch_tracing(void)
93 switch (pc_sample_pid
) {
95 pc_trace_frameworks
= FALSE
;
100 p
= proc_find(pc_sample_pid
);
112 * this only works for the current proc as it
113 * is called from context_switch in the scheduler
116 branch_tracing_enabled(void)
118 struct proc
*p
= current_proc();
119 if (TRUE
== pc_trace_frameworks
) {
135 if (!pcsample_enable
) {
139 for (i
= 0; i
< pc_trace_cnt
; i
++) {
140 pc
= pc_trace_buf
[i
];
142 if ((pcsample_beg
<= pc
) && (pc
< pcsample_end
)) {
143 if (pc_bufptr
> pc_buffer
) {
144 if ((*(pc_bufptr
- 1)) == pc
) {
145 continue; /* Ignore, probably spinning */
149 /* Then the sample is in our range */
155 /* We never wrap the buffer */
156 if ((pc_bufptr
+ pc_trace_cnt
) >= pc_buflast
) {
158 (void)disable_branch_tracing();
159 wakeup(&pcsample_enable
);
165 pcsamples_bootstrap(void)
167 if (!disable_branch_tracing()) {
171 pc_bufsize
= npcbufs
* sizeof(*pc_buffer
);
172 if (kmem_alloc(kernel_map
, &pc_buftomem
,
173 (vm_size_t
)pc_bufsize
) == KERN_SUCCESS
) {
174 pc_buffer
= (unsigned int *) pc_buftomem
;
180 pc_bufptr
= pc_buffer
;
181 pc_buflast
= &pc_bufptr
[npcbufs
];
191 pcsamples_reinit(void)
197 if (pc_bufsize
&& pc_buffer
) {
198 kmem_free(kernel_map
, (vm_offset_t
)pc_buffer
, pc_bufsize
);
201 ret
= pcsamples_bootstrap();
206 pcsamples_clear(void)
208 /* Clean up the sample buffer, set defaults */
209 global_state_pid
= -1;
211 if (pc_bufsize
&& pc_buffer
) {
212 kmem_free(kernel_map
, (vm_offset_t
)pc_buffer
, pc_bufsize
);
220 bzero((void *)pcsample_comm
, sizeof(pcsample_comm
));
221 (void)disable_branch_tracing();
223 pc_trace_frameworks
= FALSE
;
227 pcsamples_control(int *name
, __unused u_int namelen
, user_addr_t where
, size_t *sizep
)
230 size_t size
= *sizep
;
232 pcinfo_t pc_bufinfo
= {};
236 struct proc
*p
, *curproc
;
238 if (name
[0] != PCSAMPLE_GETNUMBUF
) {
239 curproc
= current_proc();
241 curpid
= curproc
->p_pid
;
246 if (global_state_pid
== -1) {
247 global_state_pid
= curpid
;
248 } else if (global_state_pid
!= curpid
) {
249 if ((p
= proc_find(global_state_pid
)) == NULL
) {
250 /* The global pid no longer exists */
251 global_state_pid
= curpid
;
254 /* The global pid exists, deny this request */
262 case PCSAMPLE_DISABLE
: /* used to disable */
265 case PCSAMPLE_SETNUMBUF
:
266 /* The buffer size is bounded by a min and max number of samples */
267 if (value
< pc_trace_cnt
) {
271 if (value
<= MAX_PCSAMPLES
) {
272 /* npcbufs = value & ~(PC_TRACE_CNT-1); */
275 npcbufs
= MAX_PCSAMPLES
;
278 case PCSAMPLE_GETNUMBUF
:
279 if (size
< sizeof(pc_bufinfo
)) {
283 pc_bufinfo
.npcbufs
= npcbufs
;
284 pc_bufinfo
.bufsize
= pc_bufsize
;
285 pc_bufinfo
.enable
= pcsample_enable
;
286 pc_bufinfo
.pcsample_beg
= pcsample_beg
;
287 pc_bufinfo
.pcsample_end
= pcsample_end
;
288 if (copyout(&pc_bufinfo
, where
, sizeof(pc_bufinfo
))) {
293 ret
= pcsamples_reinit();
295 case PCSAMPLE_REMOVE
:
298 case PCSAMPLE_READBUF
:
299 /* A nonzero value says enable and wait on the buffer */
300 /* A zero value says read up the buffer immediately */
302 /* Do not wait on the buffer */
304 (void)disable_branch_tracing();
305 ret
= pcsamples_read(where
, sizep
);
307 } else if ((pc_bufsize
<= 0) || (!pc_buffer
)) {
308 /* enable only if buffer is initialized */
313 /* Turn on branch tracing */
314 if (!enable_branch_tracing()) {
319 /* Enable sampling */
322 ret
= tsleep(&pcsample_enable
, PRIBIO
| PCATCH
, "pcsample", 0);
324 (void)disable_branch_tracing();
327 /* Eventually fix this... if (ret != EINTR) */
329 /* On errors, except EINTR, we want to cleanup buffer ptrs */
330 /* pc_bufptr = pc_buffer; */
334 /* The only way to get here is if the buffer is full */
335 ret
= pcsamples_read(where
, sizep
);
339 case PCSAMPLE_SETREG
:
340 if (size
< sizeof(pc_bufinfo
)) {
344 if (copyin(where
, &pc_bufinfo
, sizeof(pc_bufinfo
))) {
349 pcsample_beg
= pc_bufinfo
.pcsample_beg
;
350 pcsample_end
= pc_bufinfo
.pcsample_end
;
353 if (!(sizeof(pcsample_comm
) > size
)) {
357 bzero((void *)pcsample_comm
, sizeof(pcsample_comm
));
358 if (copyin(where
, pcsample_comm
, size
)) {
363 /* Check for command name or pid */
364 if (pcsample_comm
[0] != '\0') {
368 if (size
!= (2 * sizeof(pid_t
))) {
372 pidcheck
= (pid_t
*)pcsample_comm
;
373 pc_sample_pid
= pidcheck
[1];
386 * This buffer must be read up in one call.
387 * If the buffer isn't big enough to hold
388 * all the samples, it will copy up enough
389 * to fill the buffer and throw the rest away.
390 * This buffer never wraps.
393 pcsamples_read(user_addr_t buffer
, size_t *number
)
398 count
= (*number
) / sizeof(*pc_buffer
);
400 if (count
&& pc_bufsize
&& pc_buffer
) {
401 copycount
= pc_bufptr
- pc_buffer
;
403 if (copycount
<= 0) {
408 if (copycount
> count
) {
412 /* We actually have data to send up */
413 if (copyout(pc_buffer
, buffer
, copycount
* sizeof(*pc_buffer
))) {
418 pc_bufptr
= pc_buffer
;