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