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
39vm_offset_t pc_buftomem = 0;
40unsigned int * 	pc_buffer   = 0;   /* buffer that holds each pc */
41unsigned int * 	pc_bufptr   = 0;
42unsigned int * 	pc_buflast  = 0;
43unsigned int npcbufs         = 8192;      /* number of pc entries in buffer */
44unsigned int pc_bufsize      = 0;
45unsigned int pcsample_flags  = 0;
46unsigned int pcsample_enable = 0;
47
48pid_t pc_sample_pid = 0;
49boolean_t pc_trace_frameworks = FALSE;
50
51char pcsample_comm[MAXCOMLEN + 1];
52
53/* Set the default framework boundaries */
54unsigned int pcsample_beg    = 0;
55unsigned int pcsample_end    = 0;
56
57static pid_t global_state_pid = -1;       /* Used to control exclusive use of pc_buffer */
58
59extern unsigned int pc_trace_buf[];
60extern int pc_trace_cnt;
61
62void add_pcbuffer(void);
63int branch_tracing_enabled(void);
64int disable_branch_tracing(void);
65int enable_branch_tracing(void);
66int pcsamples_bootstrap(void);
67void pcsamples_clear(void);
68int pcsamples_control(int *name, u_int namelen, user_addr_t where, size_t *sizep);
69int pcsamples_read(user_addr_t buffer, size_t *number);
70int pcsamples_reinit(void);
71
72int
73enable_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  }
83  else {
84    pc_trace_frameworks = TRUE;
85  }
86
87  return 1;
88
89}
90
91int
92disable_branch_tracing(void)
93{
94    struct proc *p;
95    switch (pc_sample_pid) {
96    case -1:
97        pc_trace_frameworks = FALSE;
98        break;
99    case 0:
100        break;
101    default:
102        p = proc_find(pc_sample_pid);
103        if (p) {
104            p->p_btrace = 0;
105			proc_rele(p);
106        }
107        break;
108    }
109    clr_be_bit();
110    return 1;
111}
112
113/*
114 * this only works for the current proc as it
115 * is called from context_switch in the scheduler
116 */
117int
118branch_tracing_enabled(void)
119{
120  struct proc *p = current_proc();
121  if (TRUE == pc_trace_frameworks) return TRUE;
122  if (p) {
123    return (p->p_btrace);
124  }
125  return 0;
126}
127
128
129void
130add_pcbuffer(void)
131{
132	int      i;
133	unsigned int  pc;
134
135	if (!pcsample_enable)
136	  return;
137
138	for (i=0; i < pc_trace_cnt; i++)
139	  {
140	    pc = pc_trace_buf[i];
141
142	    if ((pcsample_beg <= pc) && (pc < pcsample_end))
143	      {
144		if (pc_bufptr > pc_buffer)
145		  {
146		    if ( (*(pc_bufptr-1)) == pc )
147		      continue;   /* Ignore, probably spinning */
148		  }
149
150		/* Then the sample is in our range */
151		*pc_bufptr = pc;
152		pc_bufptr++;
153	      }
154	  }
155
156	/* We never wrap the buffer */
157	if ((pc_bufptr + pc_trace_cnt) >= pc_buflast)
158	  {
159	    pcsample_enable = 0;
160	    (void)disable_branch_tracing();
161	    wakeup(&pcsample_enable);
162	  }
163	return;
164}
165
166int
167pcsamples_bootstrap(void)
168{
169        if (!disable_branch_tracing())
170            return(ENOTSUP);
171
172	pc_bufsize = npcbufs * sizeof(* pc_buffer);
173	if (kmem_alloc(kernel_map, &pc_buftomem,
174		       (vm_size_t)pc_bufsize) == KERN_SUCCESS)
175	  pc_buffer = (unsigned int *) pc_buftomem;
176	else
177	  pc_buffer = NULL;
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
191int
192pcsamples_reinit(void)
193{
194    int ret=0;
195
196    pcsample_enable = 0;
197
198	if (pc_bufsize && pc_buffer)
199		kmem_free(kernel_map, (vm_offset_t)pc_buffer, pc_bufsize);
200
201	ret= pcsamples_bootstrap();
202	return(ret);
203}
204
205void
206pcsamples_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	pc_buffer   = NULL;
214	pc_bufptr   = NULL;
215	pc_buflast  = NULL;
216	pc_bufsize  = 0;
217	pcsample_beg= 0;
218	pcsample_end= 0;
219	bzero((void *)pcsample_comm, sizeof(pcsample_comm));
220	(void)disable_branch_tracing();
221	pc_sample_pid = 0;
222	pc_trace_frameworks = FALSE;
223}
224
225int
226pcsamples_control(int *name, __unused u_int namelen, user_addr_t where, size_t *sizep)
227{
228    int ret=0;
229    size_t size=*sizep;
230    int value = name[1];
231    pcinfo_t pc_bufinfo;
232    pid_t *pidcheck;
233
234    pid_t curpid;
235    struct proc *p, *curproc;
236
237    if (name[0] != PCSAMPLE_GETNUMBUF)
238    {
239        curproc = current_proc();
240	    if (curproc)
241	      curpid = curproc->p_pid;
242	    else
243	      return (ESRCH);
244
245	    if (global_state_pid == -1)
246	      global_state_pid = curpid;
247	    else if (global_state_pid != curpid)
248	      {
249		if((p = proc_find(global_state_pid)) == NULL)
250		  {
251		    /* The global pid no longer exists */
252		    global_state_pid = curpid;
253		  }
254		else
255		  {
256			proc_rele(p);
257		    /* The global pid exists, deny this request */
258		    return(EBUSY);
259		  }
260        }
261    }
262
263
264	switch(name[0]) {
265    case PCSAMPLE_DISABLE:    /* used to disable */
266		  pcsample_enable=0;
267		  break;
268    case PCSAMPLE_SETNUMBUF:
269            /* The buffer size is bounded by a min and max number of samples */
270            if (value < pc_trace_cnt) {
271                ret=EINVAL;
272			     break;
273			}
274			if (value <= MAX_PCSAMPLES)
275                /*	npcbufs = value & ~(PC_TRACE_CNT-1); */
276                npcbufs = value;
277			else
278                npcbufs = MAX_PCSAMPLES;
279			break;
280    case PCSAMPLE_GETNUMBUF:
281            if (size < sizeof(pc_bufinfo)) {
282                ret=EINVAL;
283			    break;
284			}
285			pc_bufinfo.npcbufs = npcbufs;
286			pc_bufinfo.bufsize = pc_bufsize;
287			pc_bufinfo.enable = pcsample_enable;
288			pc_bufinfo.pcsample_beg = pcsample_beg;
289			pc_bufinfo.pcsample_end = pcsample_end;
290			if(copyout (&pc_bufinfo, where, sizeof(pc_bufinfo)))
291			  {
292			    ret=EINVAL;
293			  }
294			break;
295    case PCSAMPLE_SETUP:
296			ret=pcsamples_reinit();
297			break;
298    case PCSAMPLE_REMOVE:
299			pcsamples_clear();
300			break;
301    case PCSAMPLE_READBUF:
302		        /* A nonzero value says enable and wait on the buffer */
303		        /* A zero value says read up the buffer immediately */
304		        if (value == 0)
305			  {
306			    /* Do not wait on the buffer */
307			    pcsample_enable = 0;
308			    (void)disable_branch_tracing();
309			    ret = pcsamples_read(where, sizep);
310			    break;
311			  }
312		        else if ((pc_bufsize <= 0) || (!pc_buffer))
313			{
314			  /* enable only if buffer is initialized */
315			  ret=EINVAL;
316			  break;
317			}
318
319			/* Turn on branch tracing */
320			if (!enable_branch_tracing())
321			  {
322			    ret = ENOTSUP;
323			    break;
324			  }
325
326			/* Enable sampling */
327		        pcsample_enable = 1;
328
329			ret = tsleep(&pcsample_enable, PRIBIO | PCATCH, "pcsample", 0);
330			pcsample_enable = 0;
331			(void)disable_branch_tracing();
332
333			if (ret)
334			  {
335			    /*	Eventually fix this...  if (ret != EINTR) */
336			    if (ret)
337			      {
338				/* On errors, except EINTR, we want to cleanup buffer ptrs */
339				/* pc_bufptr = pc_buffer; */
340				*sizep = 0;
341			      }
342			  }
343			else
344			  {
345			    /* The only way to get here is if the buffer is full */
346			    ret = pcsamples_read(where, sizep);
347			  }
348
349			break;
350    case PCSAMPLE_SETREG:
351		        if (size < sizeof(pc_bufinfo))
352			  {
353			    ret = EINVAL;
354			    break;
355			  }
356			if (copyin(where, &pc_bufinfo, sizeof(pc_bufinfo)))
357			  {
358			    ret = EINVAL;
359			    break;
360			  }
361
362			pcsample_beg = pc_bufinfo.pcsample_beg;
363			pcsample_end = pc_bufinfo.pcsample_end;
364			break;
365    case PCSAMPLE_COMM:
366            if (!(sizeof(pcsample_comm) > size))
367            {
368                ret = EINVAL;
369                break;
370            }
371            bzero((void *)pcsample_comm, sizeof(pcsample_comm));
372			if (copyin(where, pcsample_comm, size))
373			{
374                ret = EINVAL;
375			    break;
376            }
377
378			/* Check for command name or pid */
379			if (pcsample_comm[0] != '\0')
380			{
381			    ret= ENOTSUP;
382			    break;
383            }
384			else
385			  {
386			    if (size != (2 * sizeof(pid_t)))
387			    {
388			      ret = EINVAL;
389			      break;
390			    }
391			    else
392			      {
393				pidcheck = (pid_t *)pcsample_comm;
394				pc_sample_pid = pidcheck[1];
395			      }
396			  }
397		        break;
398    default:
399		        ret= ENOTSUP;
400			break;
401	}
402	return(ret);
403}
404
405
406/*
407   This buffer must be read up in one call.
408   If the buffer isn't big enough to hold
409   all the samples, it will copy up enough
410   to fill the buffer and throw the rest away.
411   This buffer never wraps.
412*/
413int
414pcsamples_read(user_addr_t buffer, size_t *number)
415{
416    size_t count=0;
417    size_t copycount;
418
419	count = (*number)/sizeof(* pc_buffer);
420
421	if (count && pc_bufsize && pc_buffer)
422	  {
423	      copycount = pc_bufptr - pc_buffer;
424
425	      if (copycount <= 0)
426		{
427		  *number = 0;
428		  return(0);
429		}
430
431	      if (copycount > count)
432		copycount = count;
433
434	      /* We actually have data to send up */
435	      if(copyout(pc_buffer, buffer, copycount * sizeof(* pc_buffer)))
436		{
437		  *number = 0;
438		  return(EINVAL);
439		}
440	      *number = copycount;
441	      pc_bufptr = pc_buffer;
442	      return(0);
443	  }
444	else
445	  {
446	    *number = 0;
447	    return(0);
448	  }
449}
450
451