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