]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_pcsamples.c
xnu-123.5.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 * 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.h>
27 #include <sys/vm.h>
28 #include <sys/sysctl.h>
29 #include <vm/vm_kern.h>
30
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;
39
40 char pcsample_comm[MAXCOMLEN + 1];
41
42 /* Set the default framework boundaries */
43 u_long pcsample_beg = 0;
44 u_long pcsample_end = 0;
45
46 static pid_t global_state_pid = -1; /* Used to control exclusive use of pc_buffer */
47
48 extern int pc_trace_buf[];
49 extern int pc_trace_cnt;
50
51 void
52 add_pcbuffer()
53 {
54 int i;
55 u_long pc;
56 struct proc *curproc;
57 extern unsigned int kdebug_flags;
58
59 if (!pcsample_enable)
60 return;
61
62 if (pcsample_comm[0] != '\0')
63 {
64 /* If command string does not match, then return */
65 curproc = current_proc();
66 if (curproc &&
67 (strncmp(curproc->p_comm, pcsample_comm, sizeof(pcsample_comm))))
68 return;
69 }
70
71 for (i=0; i < pc_trace_cnt; i++)
72 {
73 pc = pc_trace_buf[i];
74
75 if ((pcsample_beg <= pc) && (pc < pcsample_end))
76 {
77 if (pc_bufptr > pc_buffer)
78 {
79 if ( (*(pc_bufptr-1)) == pc )
80 continue; /* Ignore, probably spinning */
81 }
82
83 /* Then the sample is in our range */
84 *pc_bufptr = (u_long)pc;
85 pc_bufptr++;
86 }
87 }
88
89 /* We never wrap the buffer */
90 if ((pc_bufptr + pc_trace_cnt) >= pc_buflast)
91 {
92 pcsample_enable = 0;
93 (void)clr_be_bit();
94 wakeup(&pcsample_enable);
95 }
96 return;
97 }
98
99 pcsamples_bootstrap()
100 {
101 if (!clr_be_bit())
102 return(ENOTSUP);
103
104 pc_bufsize = npcbufs * sizeof(* pc_buffer);
105 if (kmem_alloc(kernel_map, &pc_buftomem,
106 (vm_size_t)pc_bufsize) == KERN_SUCCESS)
107 pc_buffer = (u_long *) pc_buftomem;
108 else
109 pc_buffer= (u_long *) 0;
110
111 if (pc_buffer) {
112 pc_bufptr = pc_buffer;
113 pc_buflast = &pc_bufptr[npcbufs];
114 pcsample_enable = 0;
115 return(0);
116 } else {
117 pc_bufsize=0;
118 return(EINVAL);
119 }
120
121 }
122
123 pcsamples_reinit()
124 {
125 int x;
126 int ret=0;
127
128 pcsample_enable = 0;
129
130 if (pc_bufsize && pc_buffer)
131 kmem_free(kernel_map,pc_buffer,pc_bufsize);
132
133 ret= pcsamples_bootstrap();
134 return(ret);
135 }
136
137 pcsamples_clear()
138 {
139 /* Clean up the sample buffer, set defaults */
140 global_state_pid = -1;
141 pcsample_enable = 0;
142 if(pc_bufsize && pc_buffer)
143 kmem_free(kernel_map,pc_buffer,pc_bufsize);
144 pc_buffer = (u_long *)0;
145 pc_bufptr = (u_long *)0;
146 pc_buflast = (u_long *)0;
147 pc_bufsize = 0;
148 pcsample_beg= 0;
149 pcsample_end= 0;
150 bzero((void *)pcsample_comm, sizeof(pcsample_comm));
151 (void)clr_be_bit();
152 }
153
154 pcsamples_control(name, namelen, where, sizep)
155 int *name;
156 u_int namelen;
157 char *where;
158 size_t *sizep;
159 {
160 int ret=0;
161 int size=*sizep;
162 unsigned int value = name[1];
163 pcinfo_t pc_bufinfo;
164
165 pid_t curpid;
166 struct proc *p, *curproc;
167
168 if (name[0] != PCSAMPLE_GETNUMBUF)
169 {
170 if(curproc = current_proc())
171 curpid = curproc->p_pid;
172 else
173 return (ESRCH);
174
175 if (global_state_pid == -1)
176 global_state_pid = curpid;
177 else if (global_state_pid != curpid)
178 {
179 if((p = pfind(global_state_pid)) == NULL)
180 {
181 /* The global pid no longer exists */
182 global_state_pid = curpid;
183 }
184 else
185 {
186 /* The global pid exists, deny this request */
187 return(EBUSY);
188 }
189 }
190 }
191
192
193 switch(name[0]) {
194 case PCSAMPLE_DISABLE: /* used to disable */
195 pcsample_enable=0;
196 break;
197 case PCSAMPLE_SETNUMBUF:
198 /* The buffer size is bounded by a min and max number of samples */
199 if (value < pc_trace_cnt) {
200 ret=EINVAL;
201 break;
202 }
203 if (value <= MAX_PCSAMPLES)
204 /* npcbufs = value & ~(PC_TRACE_CNT-1); */
205 npcbufs = value;
206 else
207 npcbufs = MAX_PCSAMPLES;
208 break;
209 case PCSAMPLE_GETNUMBUF:
210 if(size < sizeof(pcinfo_t)) {
211 ret=EINVAL;
212 break;
213 }
214 pc_bufinfo.npcbufs = npcbufs;
215 pc_bufinfo.bufsize = pc_bufsize;
216 pc_bufinfo.enable = pcsample_enable;
217 pc_bufinfo.pcsample_beg = pcsample_beg;
218 pc_bufinfo.pcsample_end = pcsample_end;
219 if(copyout (&pc_bufinfo, where, sizeof(pc_bufinfo)))
220 {
221 ret=EINVAL;
222 }
223 break;
224 case PCSAMPLE_SETUP:
225 ret=pcsamples_reinit();
226 break;
227 case PCSAMPLE_REMOVE:
228 pcsamples_clear();
229 break;
230 case PCSAMPLE_READBUF:
231 /* A nonzero value says enable and wait on the buffer */
232 /* A zero value says read up the buffer immediately */
233 if (value == 0)
234 {
235 /* Do not wait on the buffer */
236 pcsample_enable = 0;
237 (void)clr_be_bit();
238 ret = pcsamples_read(where, sizep);
239 break;
240 }
241 else if ((pc_bufsize <= 0) || (!pc_buffer))
242 {
243 /* enable only if buffer is initialized */
244 ret=EINVAL;
245 break;
246 }
247
248 /* Turn on branch tracing */
249 if (!set_be_bit())
250 {
251 ret = ENOTSUP;;
252 break;
253 }
254
255 /* Enable sampling */
256 pcsample_enable = 1;
257
258 ret = tsleep(&pcsample_enable, PRIBIO | PCATCH, "pcsample", 0);
259 pcsample_enable = 0;
260 (void)clr_be_bit();
261
262 if (ret)
263 {
264 /* Eventually fix this... if (ret != EINTR) */
265 if (ret)
266 {
267 /* On errors, except EINTR, we want to cleanup buffer ptrs */
268 /* pc_bufptr = pc_buffer; */
269 *sizep = 0;
270 }
271 }
272 else
273 {
274 /* The only way to get here is if the buffer is full */
275 ret = pcsamples_read(where, sizep);
276 }
277
278 break;
279 case PCSAMPLE_SETREG:
280 if (size < sizeof(pcinfo_t))
281 {
282 ret = EINVAL;
283 break;
284 }
285 if (copyin(where, &pc_bufinfo, sizeof(pcinfo_t)))
286 {
287 ret = EINVAL;
288 break;
289 }
290
291 pcsample_beg = pc_bufinfo.pcsample_beg;
292 pcsample_end = pc_bufinfo.pcsample_end;
293 break;
294 case PCSAMPLE_COMM:
295 if (!(sizeof(pcsample_comm) > size))
296 {
297 ret = EINVAL;
298 break;
299 }
300 bzero((void *)pcsample_comm, sizeof(pcsample_comm));
301 if (copyin(where, pcsample_comm, size))
302 {
303 ret = EINVAL;
304 }
305 break;
306 default:
307 ret= EOPNOTSUPP;
308 break;
309 }
310 return(ret);
311 }
312
313
314 /*
315 This buffer must be read up in one call.
316 If the buffer isn't big enough to hold
317 all the samples, it will copy up enough
318 to fill the buffer and throw the rest away.
319 This buffer never wraps.
320 */
321 pcsamples_read(u_long *buffer, size_t *number)
322 {
323 int count=0;
324 int ret=0;
325 int copycount;
326
327 count = (*number)/sizeof(u_long);
328
329 if (count && pc_bufsize && pc_buffer)
330 {
331 copycount = pc_bufptr - pc_buffer;
332
333 if (copycount <= 0)
334 {
335 *number = 0;
336 return(0);
337 }
338
339 if (copycount > count)
340 copycount = count;
341
342 /* We actually have data to send up */
343 if(copyout(pc_buffer, buffer, copycount * sizeof(u_long)))
344 {
345 *number = 0;
346 return(EINVAL);
347 }
348 *number = copycount;
349 pc_bufptr = pc_buffer;
350 return(0);
351 }
352 else
353 {
354 *number = 0;
355 return(0);
356 }
357 }
358
359
360
361