]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/kern_pcsamples.c
xnu-3247.1.106.tar.gz
[apple/xnu.git] / bsd / kern / kern_pcsamples.c
index 68ba7cc973629593de4d1abd964174df7a273563..887029225c46e7b6b78adbd0611688e088dbe2fd 100644 (file)
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2012 Apple Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 
 #include <sys/kdebug.h>
 #include <sys/errno.h>
 #include <sys/param.h>
-#include <sys/proc.h>
+#include <sys/proc_internal.h>
 #include <sys/vm.h>
 #include <sys/sysctl.h>
+#include <sys/systm.h>
 #include <vm/vm_kern.h>
+#include <machine/machine_routines.h>
 
-unsigned int pc_buftomem = 0;
-u_long     * pc_buffer   = 0;   /* buffer that holds each pc */
-u_long     * pc_bufptr   = 0;
-u_long     * pc_buflast  = 0;
+vm_offset_t pc_buftomem = 0;
+unsigned int *         pc_buffer   = 0;   /* buffer that holds each pc */
+unsigned int *         pc_bufptr   = 0;
+unsigned int *         pc_buflast  = 0;
 unsigned int npcbufs         = 8192;      /* number of pc entries in buffer */
 unsigned int pc_bufsize      = 0;
 unsigned int pcsample_flags  = 0;
 unsigned int pcsample_enable = 0;
 
+pid_t pc_sample_pid = 0;
+boolean_t pc_trace_frameworks = FALSE;
+
 char pcsample_comm[MAXCOMLEN + 1];
 
 /* Set the default framework boundaries */
-u_long pcsample_beg    = 0;
-u_long pcsample_end    = 0;
+unsigned int pcsample_beg    = 0;
+unsigned int pcsample_end    = 0;
 
 static pid_t global_state_pid = -1;       /* Used to control exclusive use of pc_buffer */
 
-extern int pc_trace_buf[];
+extern unsigned int pc_trace_buf[];
 extern int pc_trace_cnt;
 
+void add_pcbuffer(void);
+int branch_tracing_enabled(void);
+int disable_branch_tracing(void);
+int enable_branch_tracing(void);
+int pcsamples_bootstrap(void);
+void pcsamples_clear(void);
+int pcsamples_control(int *name, u_int namelen, user_addr_t where, size_t *sizep);
+int pcsamples_read(user_addr_t buffer, size_t *number);
+int pcsamples_reinit(void);
+
+int
+enable_branch_tracing(void)
+{
+  struct proc *p;
+  if (-1 != pc_sample_pid) {
+    p = proc_find(pc_sample_pid);
+    if (p) {
+      p->p_btrace = 1;
+        proc_rele(p);
+    } 
+  }
+  else {
+    pc_trace_frameworks = TRUE;
+  }
+
+  return 1;
+
+}
+
+int
+disable_branch_tracing(void)
+{
+    struct proc *p;
+    switch (pc_sample_pid) {
+    case -1:
+        pc_trace_frameworks = FALSE;
+        break;
+    case 0:
+        break;
+    default:
+        p = proc_find(pc_sample_pid);
+        if (p) {
+            p->p_btrace = 0;
+                       proc_rele(p);
+        }
+        break;
+    }
+    clr_be_bit();
+    return 1;
+}
+
+/*
+ * this only works for the current proc as it
+ * is called from context_switch in the scheduler
+ */
+int
+branch_tracing_enabled(void)
+{
+  struct proc *p = current_proc();
+  if (TRUE == pc_trace_frameworks) return TRUE;
+  if (p) {
+    return (p->p_btrace);
+  }
+  return 0;
+}
+
+
 void
-add_pcbuffer()
+add_pcbuffer(void)
 {
        int      i;
-       u_long  pc;
-       struct proc *curproc;
-       extern unsigned int kdebug_flags;
+       unsigned int  pc; 
 
        if (!pcsample_enable)
          return;
 
-       if (pcsample_comm[0] != '\0')
-       {
-         /* If command string does not match, then return */
-           curproc = current_proc();
-           if (curproc && 
-               (strncmp(curproc->p_comm, pcsample_comm, sizeof(pcsample_comm))))
-             return;
-       }
-
        for (i=0; i < pc_trace_cnt; i++)
          {
            pc = pc_trace_buf[i];
@@ -81,7 +148,7 @@ add_pcbuffer()
                  }
 
                /* Then the sample is in our range */
-               *pc_bufptr = (u_long)pc;
+               *pc_bufptr = pc;
                pc_bufptr++;
              }
          }
@@ -90,23 +157,24 @@ add_pcbuffer()
        if ((pc_bufptr + pc_trace_cnt) >= pc_buflast)
          {
            pcsample_enable = 0;
-           (void)clr_be_bit();
+           (void)disable_branch_tracing();
            wakeup(&pcsample_enable);
          }
        return;
 }
 
-pcsamples_bootstrap()
+int
+pcsamples_bootstrap(void)
 {
-       if (!clr_be_bit())
+        if (!disable_branch_tracing())
             return(ENOTSUP);
 
        pc_bufsize = npcbufs * sizeof(* pc_buffer);
        if (kmem_alloc(kernel_map, &pc_buftomem,
                       (vm_size_t)pc_bufsize) == KERN_SUCCESS) 
-         pc_buffer = (u_long *) pc_buftomem;
+         pc_buffer = (unsigned int *) pc_buftomem;
        else 
-         pc_buffer= (u_long *) 0;
+         pc_buffer = NULL;
 
        if (pc_buffer) {
                pc_bufptr = pc_buffer;
@@ -120,54 +188,56 @@ pcsamples_bootstrap()
        
 }
 
-pcsamples_reinit()
+int
+pcsamples_reinit(void)
 {
-int x;
-int ret=0;
+    int ret=0;
 
-        pcsample_enable = 0;
+    pcsample_enable = 0;
 
        if (pc_bufsize && pc_buffer)
-               kmem_free(kernel_map,pc_buffer,pc_bufsize);
+               kmem_free(kernel_map, (vm_offset_t)pc_buffer, pc_bufsize);
 
        ret= pcsamples_bootstrap();
        return(ret);
 }
 
-pcsamples_clear()
+void
+pcsamples_clear(void)
 {
-        /* Clean up the sample buffer, set defaults */ 
-        global_state_pid = -1;
+    /* Clean up the sample buffer, set defaults */ 
+    global_state_pid = -1;
        pcsample_enable = 0;
        if(pc_bufsize && pc_buffer)
-         kmem_free(kernel_map,pc_buffer,pc_bufsize);
-       pc_buffer   = (u_long *)0;
-       pc_bufptr   = (u_long *)0;
-       pc_buflast  = (u_long *)0;
+         kmem_free(kernel_map, (vm_offset_t)pc_buffer, pc_bufsize);
+       pc_buffer   = NULL;
+       pc_bufptr   = NULL;
+       pc_buflast  = NULL;
        pc_bufsize  = 0;
        pcsample_beg= 0;
        pcsample_end= 0;
        bzero((void *)pcsample_comm, sizeof(pcsample_comm));
-       (void)clr_be_bit();
+       (void)disable_branch_tracing();
+       pc_sample_pid = 0;
+       pc_trace_frameworks = FALSE;
 }
 
-pcsamples_control(name, namelen, where, sizep)
-int *name;
-u_int namelen;
-char *where;
-size_t *sizep;
+int
+pcsamples_control(int *name, __unused u_int namelen, user_addr_t where, size_t *sizep)
 {
-int ret=0;
-int size=*sizep;
-unsigned int value = name[1];
-pcinfo_t pc_bufinfo;
-
-pid_t curpid;
-struct proc *p, *curproc;
-
-        if (name[0] != PCSAMPLE_GETNUMBUF)
-         { 
-           if(curproc = current_proc())
+    int ret=0;
+    size_t size=*sizep;
+    int value = name[1];
+    pcinfo_t pc_bufinfo;
+    pid_t *pidcheck;
+
+    pid_t curpid;
+    struct proc *p, *curproc;
+
+    if (name[0] != PCSAMPLE_GETNUMBUF)
+    { 
+        curproc = current_proc();
+           if (curproc)
              curpid = curproc->p_pid;
            else
              return (ESRCH);
@@ -176,39 +246,40 @@ struct proc *p, *curproc;
              global_state_pid = curpid;
            else if (global_state_pid != curpid)
              {
-               if((p = pfind(global_state_pid)) == NULL)
+               if((p = proc_find(global_state_pid)) == NULL)
                  {
                    /* The global pid no longer exists */
                    global_state_pid = curpid;
                  }
                else
                  {
+                       proc_rele(p);
                    /* The global pid exists, deny this request */
                    return(EBUSY);
                  }
-             }
-         }
+        }
+    }
 
 
        switch(name[0]) {
-               case PCSAMPLE_DISABLE:    /* used to disable */
+    case PCSAMPLE_DISABLE:    /* used to disable */
                  pcsample_enable=0;
                  break;
-               case PCSAMPLE_SETNUMBUF:
-                       /* The buffer size is bounded by a min and max number of samples */
-                       if (value < pc_trace_cnt) {
-                            ret=EINVAL;
+    case PCSAMPLE_SETNUMBUF:
+            /* The buffer size is bounded by a min and max number of samples */
+            if (value < pc_trace_cnt) {
+                ret=EINVAL;
                             break;
                        }
                        if (value <= MAX_PCSAMPLES)
-                         /*    npcbufs = value & ~(PC_TRACE_CNT-1); */
-                         npcbufs = value;
+                /*     npcbufs = value & ~(PC_TRACE_CNT-1); */
+                npcbufs = value;
                        else
-                         npcbufs = MAX_PCSAMPLES;
+                npcbufs = MAX_PCSAMPLES;
                        break;
-               case PCSAMPLE_GETNUMBUF:
-                       if(size < sizeof(pcinfo_t)) {
-                           ret=EINVAL;
+    case PCSAMPLE_GETNUMBUF:
+            if (size < sizeof(pc_bufinfo)) {
+                ret=EINVAL;
                            break;
                        }
                        pc_bufinfo.npcbufs = npcbufs;
@@ -221,20 +292,20 @@ struct proc *p, *curproc;
                            ret=EINVAL;
                          }
                        break;
-               case PCSAMPLE_SETUP:
+    case PCSAMPLE_SETUP:
                        ret=pcsamples_reinit();
                        break;
-               case PCSAMPLE_REMOVE:
+    case PCSAMPLE_REMOVE:
                        pcsamples_clear();
                        break;
-               case PCSAMPLE_READBUF:
+    case PCSAMPLE_READBUF:
                        /* A nonzero value says enable and wait on the buffer */
                        /* A zero value says read up the buffer immediately */
                        if (value == 0)
                          {
                            /* Do not wait on the buffer */
                            pcsample_enable = 0;
-                           (void)clr_be_bit();
+                           (void)disable_branch_tracing();
                            ret = pcsamples_read(where, sizep);
                            break;
                          }
@@ -246,9 +317,9 @@ struct proc *p, *curproc;
                        }
 
                        /* Turn on branch tracing */
-                       if (!set_be_bit())
+                       if (!enable_branch_tracing())
                          {
-                           ret = ENOTSUP;;
+                           ret = ENOTSUP;
                            break;
                          }
 
@@ -257,7 +328,7 @@ struct proc *p, *curproc;
 
                        ret = tsleep(&pcsample_enable, PRIBIO | PCATCH, "pcsample", 0);
                        pcsample_enable = 0;
-                       (void)clr_be_bit();
+                       (void)disable_branch_tracing();
 
                        if (ret)
                          {
@@ -276,13 +347,13 @@ struct proc *p, *curproc;
                          }
 
                        break;
-               case PCSAMPLE_SETREG:
-                       if (size < sizeof(pcinfo_t))
+    case PCSAMPLE_SETREG:
+                       if (size < sizeof(pc_bufinfo))
                          {
                            ret = EINVAL;
                            break;
                          }
-                       if (copyin(where, &pc_bufinfo, sizeof(pcinfo_t)))
+                       if (copyin(where, &pc_bufinfo, sizeof(pc_bufinfo)))
                          {
                            ret = EINVAL;
                            break;
@@ -291,20 +362,41 @@ struct proc *p, *curproc;
                        pcsample_beg = pc_bufinfo.pcsample_beg;
                        pcsample_end = pc_bufinfo.pcsample_end;
                        break;
-               case PCSAMPLE_COMM:
-                       if (!(sizeof(pcsample_comm) > size))
-                       {
-                           ret = EINVAL;
-                           break;
-                       }
-                       bzero((void *)pcsample_comm, sizeof(pcsample_comm));
-                       if (copyin(where, pcsample_comm, size))
-                       {
-                           ret = EINVAL;
-                       }
+    case PCSAMPLE_COMM:
+            if (!(sizeof(pcsample_comm) > size)) 
+            {
+                ret = EINVAL;
+                break;
+            }
+            bzero((void *)pcsample_comm, sizeof(pcsample_comm));
+                       if (copyin(where, pcsample_comm, size)) 
+                       {
+                ret = EINVAL;
+                           break;
+            }
+
+                       /* Check for command name or pid */
+                       if (pcsample_comm[0] != '\0') 
+                       {
+                           ret= ENOTSUP;
+                           break;
+            }
+                       else
+                         {
+                           if (size != (2 * sizeof(pid_t)))
+                           {
+                             ret = EINVAL;
+                             break;
+                           }
+                           else
+                             {
+                               pidcheck = (pid_t *)pcsample_comm;
+                               pc_sample_pid = pidcheck[1];
+                             }
+                         }
                        break;
-               default:
-                       ret= EOPNOTSUPP;
+    default:
+                       ret= ENOTSUP;
                        break;
        }
        return(ret);
@@ -318,13 +410,13 @@ struct proc *p, *curproc;
    to fill the buffer and throw the rest away.
    This buffer never wraps.
 */
-pcsamples_read(u_long *buffer, size_t *number)
+int
+pcsamples_read(user_addr_t buffer, size_t *number)
 {
-int count=0;
-int ret=0;
-int copycount;
+    size_t count=0;
+    size_t copycount;
 
-       count = (*number)/sizeof(u_long);
+       count = (*number)/sizeof(* pc_buffer);
 
        if (count && pc_bufsize && pc_buffer)
          {
@@ -340,7 +432,7 @@ int copycount;
                copycount = count;
 
              /* We actually have data to send up */
-             if(copyout(pc_buffer, buffer, copycount * sizeof(u_long)))
+             if(copyout(pc_buffer, buffer, copycount * sizeof(* pc_buffer)))
                {
                  *number = 0;
                  return(EINVAL);
@@ -356,6 +448,3 @@ int copycount;
          }
 }
 
-
-
-