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