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