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>
28 #include <sys/sysctl.h>
29 #include <vm/vm_kern.h>
31 unsigned int pc_buftomem
= 0;
32 u_long
* pc_buffer
= 0; /* buffer that holds each pc */
33 u_long
* pc_bufptr
= 0;
34 u_long
* pc_buflast
= 0;
35 unsigned int npcbufs
= 8192; /* number of pc entries in buffer */
36 unsigned int pc_bufsize
= 0;
37 unsigned int pcsample_flags
= 0;
38 unsigned int pcsample_enable
= 0;
40 pid_t pc_sample_pid
= 0;
41 boolean_t pc_trace_frameworks
= FALSE
;
43 char pcsample_comm
[MAXCOMLEN
+ 1];
45 /* Set the default framework boundaries */
46 u_long pcsample_beg
= 0;
47 u_long pcsample_end
= 0;
49 static pid_t global_state_pid
= -1; /* Used to control exclusive use of pc_buffer */
51 extern int pc_trace_buf
[];
52 extern int pc_trace_cnt
;
55 enable_branch_tracing()
59 if (-1 != pc_sample_pid
) {
60 p
= pfind(pc_sample_pid
);
62 p
->p_flag
|= P_BTRACE
;
66 pc_trace_frameworks
= TRUE
;
77 disable_branch_tracing()
80 switch (pc_sample_pid
) {
82 pc_trace_frameworks
= FALSE
;
87 p
= pfind(pc_sample_pid
);
89 p
->p_flag
&= ~P_BTRACE
;
98 * this only works for the current proc as it
99 * is called from context_switch in the scheduler
102 branch_tracing_enabled()
104 struct proc
*p
= current_proc();
105 if (TRUE
== pc_trace_frameworks
) return TRUE
;
107 return (P_BTRACE
== (p
->p_flag
& P_BTRACE
));
118 struct proc
*curproc
;
119 extern unsigned int kdebug_flags
;
121 if (!pcsample_enable
)
124 for (i
=0; i
< pc_trace_cnt
; i
++)
126 pc
= pc_trace_buf
[i
];
128 if ((pcsample_beg
<= pc
) && (pc
< pcsample_end
))
130 if (pc_bufptr
> pc_buffer
)
132 if ( (*(pc_bufptr
-1)) == pc
)
133 continue; /* Ignore, probably spinning */
136 /* Then the sample is in our range */
137 *pc_bufptr
= (u_long
)pc
;
142 /* We never wrap the buffer */
143 if ((pc_bufptr
+ pc_trace_cnt
) >= pc_buflast
)
146 (void)disable_branch_tracing();
147 wakeup(&pcsample_enable
);
152 pcsamples_bootstrap()
154 if (!disable_branch_tracing())
157 pc_bufsize
= npcbufs
* sizeof(* pc_buffer
);
158 if (kmem_alloc(kernel_map
, &pc_buftomem
,
159 (vm_size_t
)pc_bufsize
) == KERN_SUCCESS
)
160 pc_buffer
= (u_long
*) pc_buftomem
;
162 pc_buffer
= (u_long
*) 0;
165 pc_bufptr
= pc_buffer
;
166 pc_buflast
= &pc_bufptr
[npcbufs
];
183 if (pc_bufsize
&& pc_buffer
)
184 kmem_free(kernel_map
, (vm_offset_t
)pc_buffer
, pc_bufsize
);
186 ret
= pcsamples_bootstrap();
192 /* Clean up the sample buffer, set defaults */
193 global_state_pid
= -1;
195 if(pc_bufsize
&& pc_buffer
)
196 kmem_free(kernel_map
, (vm_offset_t
)pc_buffer
, pc_bufsize
);
197 pc_buffer
= (u_long
*)0;
198 pc_bufptr
= (u_long
*)0;
199 pc_buflast
= (u_long
*)0;
203 bzero((void *)pcsample_comm
, sizeof(pcsample_comm
));
204 (void)disable_branch_tracing();
206 pc_trace_frameworks
= FALSE
;
210 pcsamples_control(name
, namelen
, where
, sizep
)
218 unsigned int value
= name
[1];
223 struct proc
*p
, *curproc
;
225 if (name
[0] != PCSAMPLE_GETNUMBUF
)
227 if(curproc
= current_proc())
228 curpid
= curproc
->p_pid
;
232 if (global_state_pid
== -1)
233 global_state_pid
= curpid
;
234 else if (global_state_pid
!= curpid
)
236 if((p
= pfind(global_state_pid
)) == NULL
)
238 /* The global pid no longer exists */
239 global_state_pid
= curpid
;
243 /* The global pid exists, deny this request */
251 case PCSAMPLE_DISABLE
: /* used to disable */
254 case PCSAMPLE_SETNUMBUF
:
255 /* The buffer size is bounded by a min and max number of samples */
256 if (value
< pc_trace_cnt
) {
260 if (value
<= MAX_PCSAMPLES
)
261 /* npcbufs = value & ~(PC_TRACE_CNT-1); */
264 npcbufs
= MAX_PCSAMPLES
;
266 case PCSAMPLE_GETNUMBUF
:
267 if(size
< sizeof(pcinfo_t
)) {
271 pc_bufinfo
.npcbufs
= npcbufs
;
272 pc_bufinfo
.bufsize
= pc_bufsize
;
273 pc_bufinfo
.enable
= pcsample_enable
;
274 pc_bufinfo
.pcsample_beg
= pcsample_beg
;
275 pc_bufinfo
.pcsample_end
= pcsample_end
;
276 if(copyout (&pc_bufinfo
, where
, sizeof(pc_bufinfo
)))
282 ret
=pcsamples_reinit();
284 case PCSAMPLE_REMOVE
:
287 case PCSAMPLE_READBUF
:
288 /* A nonzero value says enable and wait on the buffer */
289 /* A zero value says read up the buffer immediately */
292 /* Do not wait on the buffer */
294 (void)disable_branch_tracing();
295 ret
= pcsamples_read(where
, sizep
);
298 else if ((pc_bufsize
<= 0) || (!pc_buffer
))
300 /* enable only if buffer is initialized */
305 /* Turn on branch tracing */
306 if (!enable_branch_tracing())
312 /* Enable sampling */
315 ret
= tsleep(&pcsample_enable
, PRIBIO
| PCATCH
, "pcsample", 0);
317 (void)disable_branch_tracing();
321 /* Eventually fix this... if (ret != EINTR) */
324 /* On errors, except EINTR, we want to cleanup buffer ptrs */
325 /* pc_bufptr = pc_buffer; */
331 /* The only way to get here is if the buffer is full */
332 ret
= pcsamples_read(where
, sizep
);
336 case PCSAMPLE_SETREG
:
337 if (size
< sizeof(pcinfo_t
))
342 if (copyin(where
, &pc_bufinfo
, sizeof(pcinfo_t
)))
348 pcsample_beg
= pc_bufinfo
.pcsample_beg
;
349 pcsample_end
= pc_bufinfo
.pcsample_end
;
352 if (!(sizeof(pcsample_comm
) > size
))
357 bzero((void *)pcsample_comm
, sizeof(pcsample_comm
));
358 if (copyin(where
, pcsample_comm
, size
))
364 /* Check for command name or pid */
365 if (pcsample_comm
[0] != '\0')
372 if (size
!= (2 * sizeof(pid_t
)))
379 pidcheck
= (pid_t
*)pcsample_comm
;
380 pc_sample_pid
= pidcheck
[1];
393 This buffer must be read up in one call.
394 If the buffer isn't big enough to hold
395 all the samples, it will copy up enough
396 to fill the buffer and throw the rest away.
397 This buffer never wraps.
399 pcsamples_read(u_long
*buffer
, size_t *number
)
405 count
= (*number
)/sizeof(u_long
);
407 if (count
&& pc_bufsize
&& pc_buffer
)
409 copycount
= pc_bufptr
- pc_buffer
;
417 if (copycount
> count
)
420 /* We actually have data to send up */
421 if(copyout(pc_buffer
, buffer
, copycount
* sizeof(u_long
)))
427 pc_bufptr
= pc_buffer
;