X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..3e170ce000f1506b7b5d2c5c7faec85ceabb573d:/bsd/kern/kern_pcsamples.c?ds=sidebyside diff --git a/bsd/kern/kern_pcsamples.c b/bsd/kern/kern_pcsamples.c index 68ba7cc97..887029225 100644 --- a/bsd/kern/kern_pcsamples.c +++ b/bsd/kern/kern_pcsamples.c @@ -1,73 +1,140 @@ /* - * 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 #include #include -#include +#include #include #include +#include #include +#include -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; } } - - -