2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/kdebug.h>
24 #include <sys/errno.h>
25 #include <sys/param.h>
26 #include <sys/proc_internal.h>
28 #include <sys/sysctl.h>
29 #include <sys/systm.h>
30 #include <vm/vm_kern.h>
31 #include <machine/machine_routines.h>
33 vm_offset_t pc_buftomem
= 0;
34 unsigned int * pc_buffer
= 0; /* buffer that holds each pc */
35 unsigned int * pc_bufptr
= 0;
36 unsigned int * pc_buflast
= 0;
37 unsigned int npcbufs
= 8192; /* number of pc entries in buffer */
38 unsigned int pc_bufsize
= 0;
39 unsigned int pcsample_flags
= 0;
40 unsigned int pcsample_enable
= 0;
42 pid_t pc_sample_pid
= 0;
43 boolean_t pc_trace_frameworks
= FALSE
;
45 char pcsample_comm
[MAXCOMLEN
+ 1];
47 /* Set the default framework boundaries */
48 unsigned int pcsample_beg
= 0;
49 unsigned int pcsample_end
= 0;
51 static pid_t global_state_pid
= -1; /* Used to control exclusive use of pc_buffer */
53 extern int pc_trace_buf
[];
54 extern int pc_trace_cnt
;
56 void add_pcbuffer(void);
57 int branch_tracing_enabled(void);
58 int disable_branch_tracing(void);
59 int enable_branch_tracing(void);
60 int pcsamples_bootstrap(void);
61 void pcsamples_clear(void);
62 int pcsamples_control(int *name
, u_int namelen
, user_addr_t where
, size_t *sizep
);
63 int pcsamples_read(user_addr_t buffer
, size_t *number
);
64 int pcsamples_reinit(void);
67 enable_branch_tracing(void)
71 if (-1 != pc_sample_pid
) {
72 p
= pfind(pc_sample_pid
);
74 p
->p_flag
|= P_BTRACE
;
78 pc_trace_frameworks
= TRUE
;
89 disable_branch_tracing(void)
92 switch (pc_sample_pid
) {
94 pc_trace_frameworks
= FALSE
;
99 p
= pfind(pc_sample_pid
);
101 p
->p_flag
&= ~P_BTRACE
;
110 * this only works for the current proc as it
111 * is called from context_switch in the scheduler
114 branch_tracing_enabled(void)
116 struct proc
*p
= current_proc();
117 if (TRUE
== pc_trace_frameworks
) return TRUE
;
119 return (P_BTRACE
== (p
->p_flag
& P_BTRACE
));
131 if (!pcsample_enable
)
134 for (i
=0; i
< pc_trace_cnt
; i
++)
136 pc
= pc_trace_buf
[i
];
138 if ((pcsample_beg
<= pc
) && (pc
< pcsample_end
))
140 if (pc_bufptr
> pc_buffer
)
142 if ( (*(pc_bufptr
-1)) == pc
)
143 continue; /* Ignore, probably spinning */
146 /* Then the sample is in our range */
152 /* We never wrap the buffer */
153 if ((pc_bufptr
+ pc_trace_cnt
) >= pc_buflast
)
156 (void)disable_branch_tracing();
157 wakeup(&pcsample_enable
);
163 pcsamples_bootstrap(void)
165 if (!disable_branch_tracing())
168 pc_bufsize
= npcbufs
* sizeof(* pc_buffer
);
169 if (kmem_alloc(kernel_map
, &pc_buftomem
,
170 (vm_size_t
)pc_bufsize
) == KERN_SUCCESS
)
171 pc_buffer
= (unsigned int *) pc_buftomem
;
176 pc_bufptr
= pc_buffer
;
177 pc_buflast
= &pc_bufptr
[npcbufs
];
188 pcsamples_reinit(void)
194 if (pc_bufsize
&& pc_buffer
)
195 kmem_free(kernel_map
, (vm_offset_t
)pc_buffer
, pc_bufsize
);
197 ret
= pcsamples_bootstrap();
202 pcsamples_clear(void)
204 /* Clean up the sample buffer, set defaults */
205 global_state_pid
= -1;
207 if(pc_bufsize
&& pc_buffer
)
208 kmem_free(kernel_map
, (vm_offset_t
)pc_buffer
, pc_bufsize
);
215 bzero((void *)pcsample_comm
, sizeof(pcsample_comm
));
216 (void)disable_branch_tracing();
218 pc_trace_frameworks
= FALSE
;
222 pcsamples_control(int *name
, __unused u_int namelen
, user_addr_t where
, size_t *sizep
)
231 struct proc
*p
, *curproc
;
233 if (name
[0] != PCSAMPLE_GETNUMBUF
)
235 curproc
= current_proc();
237 curpid
= curproc
->p_pid
;
241 if (global_state_pid
== -1)
242 global_state_pid
= curpid
;
243 else if (global_state_pid
!= curpid
)
245 if((p
= pfind(global_state_pid
)) == NULL
)
247 /* The global pid no longer exists */
248 global_state_pid
= curpid
;
252 /* The global pid exists, deny this request */
260 case PCSAMPLE_DISABLE
: /* used to disable */
263 case PCSAMPLE_SETNUMBUF
:
264 /* The buffer size is bounded by a min and max number of samples */
265 if (value
< pc_trace_cnt
) {
269 if (value
<= MAX_PCSAMPLES
)
270 /* npcbufs = value & ~(PC_TRACE_CNT-1); */
273 npcbufs
= MAX_PCSAMPLES
;
275 case PCSAMPLE_GETNUMBUF
:
276 if (size
< sizeof(pc_bufinfo
)) {
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
)))
291 ret
=pcsamples_reinit();
293 case PCSAMPLE_REMOVE
:
296 case PCSAMPLE_READBUF
:
297 /* A nonzero value says enable and wait on the buffer */
298 /* A zero value says read up the buffer immediately */
301 /* Do not wait on the buffer */
303 (void)disable_branch_tracing();
304 ret
= pcsamples_read(where
, sizep
);
307 else if ((pc_bufsize
<= 0) || (!pc_buffer
))
309 /* enable only if buffer is initialized */
314 /* Turn on branch tracing */
315 if (!enable_branch_tracing())
321 /* Enable sampling */
324 ret
= tsleep(&pcsample_enable
, PRIBIO
| PCATCH
, "pcsample", 0);
326 (void)disable_branch_tracing();
330 /* Eventually fix this... if (ret != EINTR) */
333 /* On errors, except EINTR, we want to cleanup buffer ptrs */
334 /* pc_bufptr = pc_buffer; */
340 /* The only way to get here is if the buffer is full */
341 ret
= pcsamples_read(where
, sizep
);
345 case PCSAMPLE_SETREG
:
346 if (size
< sizeof(pc_bufinfo
))
351 if (copyin(where
, &pc_bufinfo
, sizeof(pc_bufinfo
)))
357 pcsample_beg
= pc_bufinfo
.pcsample_beg
;
358 pcsample_end
= pc_bufinfo
.pcsample_end
;
361 if (!(sizeof(pcsample_comm
) > size
))
366 bzero((void *)pcsample_comm
, sizeof(pcsample_comm
));
367 if (copyin(where
, pcsample_comm
, size
))
373 /* Check for command name or pid */
374 if (pcsample_comm
[0] != '\0')
381 if (size
!= (2 * sizeof(pid_t
)))
388 pidcheck
= (pid_t
*)pcsample_comm
;
389 pc_sample_pid
= pidcheck
[1];
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.
409 pcsamples_read(user_addr_t buffer
, size_t *number
)
414 count
= (*number
)/sizeof(* pc_buffer
);
416 if (count
&& pc_bufsize
&& pc_buffer
)
418 copycount
= pc_bufptr
- pc_buffer
;
426 if (copycount
> count
)
429 /* We actually have data to send up */
430 if(copyout(pc_buffer
, buffer
, copycount
* sizeof(* pc_buffer
)))
436 pc_bufptr
= pc_buffer
;