Commit | Line | Data |
---|---|---|
316670eb A |
1 | /* |
2 | * Copyright (c) 2011 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 | #include <mach/mach_types.h> | |
29 | #include <kern/thread.h> | |
30 | #include <kern/machine.h> | |
31 | #include <kern/kalloc.h> | |
32 | #include <sys/errno.h> | |
33 | ||
316670eb | 34 | #include <kperf/sample.h> |
316670eb A |
35 | #include <kperf/pet.h> |
36 | #include <kperf/action.h> | |
37 | #include <kperf/kperf.h> | |
38 | #include <kperf/timetrigger.h> | |
39 | ||
39236c6e A |
40 | #include <kern/ipc_tt.h> /* port_name_to_task */ |
41 | ||
316670eb A |
42 | /** misc functions **/ |
43 | #include <chud/chud_xnu.h> /* XXX: should bust this out */ | |
44 | ||
39236c6e A |
45 | /* thread on CPUs before starting the PET thread */ |
46 | thread_t *kperf_thread_on_cpus = NULL; | |
47 | ||
48 | /* interupt sample buffers -- one wired per CPU */ | |
316670eb A |
49 | static struct kperf_sample *intr_samplev = NULL; |
50 | static unsigned intr_samplec = 0; | |
39236c6e A |
51 | |
52 | /* track recursion in the trace code */ | |
53 | static struct | |
54 | { | |
55 | int active; | |
56 | int pad[64 / sizeof(int)]; | |
57 | } *kpdbg_recursev; | |
58 | static unsigned kpdbg_recursec = 0; | |
59 | ||
60 | /* Curren sampling status */ | |
316670eb | 61 | static unsigned sampling_status = KPERF_SAMPLING_OFF; |
316670eb | 62 | |
39236c6e A |
63 | /* Make sure we only init once */ |
64 | static unsigned kperf_initted = 0; | |
316670eb A |
65 | |
66 | extern void (*chudxnu_thread_ast_handler)(thread_t); | |
67 | ||
68 | struct kperf_sample* | |
69 | kperf_intr_sample_buffer(void) | |
70 | { | |
71 | unsigned ncpu = chudxnu_cpu_number(); | |
72 | ||
73 | // XXX: assert? | |
74 | if( ncpu >= intr_samplec ) | |
75 | return NULL; | |
76 | ||
77 | return &intr_samplev[ncpu]; | |
78 | } | |
79 | ||
39236c6e A |
80 | int |
81 | kperf_kdbg_recurse(int step) | |
82 | { | |
83 | unsigned ncpu = chudxnu_cpu_number(); | |
84 | ||
85 | // XXX: assert? | |
86 | if( ncpu >= kpdbg_recursec ) | |
87 | return 1; | |
88 | ||
89 | /* recursing in, available */ | |
90 | if( (step > 0) | |
91 | && (kpdbg_recursev[ncpu].active == 0) ) | |
92 | { | |
93 | kpdbg_recursev[ncpu].active = 1; | |
94 | return 0; | |
95 | } | |
96 | ||
97 | /* recursing in, unavailable */ | |
98 | if( (step > 0) | |
99 | && (kpdbg_recursev[ncpu].active != 0) ) | |
100 | { | |
101 | return 1; | |
102 | } | |
103 | ||
104 | /* recursing out, unavailable */ | |
105 | if( (step < 0) | |
106 | && (kpdbg_recursev[ncpu].active != 0) ) | |
107 | { | |
108 | kpdbg_recursev[ncpu].active = 0; | |
109 | return 0; | |
110 | } | |
111 | ||
112 | /* recursing out, available */ | |
113 | if( (step < 0) | |
114 | && (kpdbg_recursev[ncpu].active == 0) ) | |
115 | panic( "return from non-recursed kperf kdebug call" ); | |
116 | ||
117 | panic( "unknown kperf kdebug call" ); | |
118 | return 1; | |
119 | } | |
120 | ||
316670eb A |
121 | /* setup interrupt sample buffers */ |
122 | int | |
123 | kperf_init(void) | |
124 | { | |
125 | unsigned ncpus = 0; | |
39236c6e | 126 | int err; |
316670eb A |
127 | |
128 | if( kperf_initted ) | |
129 | return 0; | |
130 | ||
131 | /* get number of cpus */ | |
132 | ncpus = machine_info.logical_cpu_max; | |
133 | ||
39236c6e A |
134 | kperf_thread_on_cpus = kalloc( ncpus * sizeof(*kperf_thread_on_cpus) ); |
135 | if( kperf_thread_on_cpus == NULL ) | |
136 | { | |
137 | err = ENOMEM; | |
138 | goto error; | |
139 | } | |
140 | ||
141 | /* clear it */ | |
142 | bzero( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) ); | |
143 | ||
144 | /* make the CPU array | |
316670eb A |
145 | * FIXME: cache alignment |
146 | */ | |
147 | intr_samplev = kalloc( ncpus * sizeof(*intr_samplev)); | |
39236c6e | 148 | intr_samplec = ncpus; |
316670eb A |
149 | |
150 | if( intr_samplev == NULL ) | |
39236c6e A |
151 | { |
152 | err = ENOMEM; | |
153 | goto error; | |
154 | } | |
316670eb A |
155 | |
156 | /* clear it */ | |
157 | bzero( intr_samplev, ncpus * sizeof(*intr_samplev) ); | |
39236c6e A |
158 | |
159 | /* make the recursion array */ | |
160 | kpdbg_recursev = kalloc( ncpus * sizeof(*kpdbg_recursev)); | |
161 | kpdbg_recursec = ncpus; | |
162 | ||
163 | /* clear it */ | |
164 | bzero( kpdbg_recursev, ncpus * sizeof(*kpdbg_recursev) ); | |
316670eb A |
165 | |
166 | /* we're done */ | |
316670eb A |
167 | kperf_initted = 1; |
168 | ||
169 | return 0; | |
39236c6e A |
170 | error: |
171 | if( intr_samplev ) | |
172 | kfree( intr_samplev, ncpus * sizeof(*intr_samplev) ); | |
173 | if( kperf_thread_on_cpus ) | |
174 | kfree( kperf_thread_on_cpus, ncpus * sizeof(*kperf_thread_on_cpus) ); | |
175 | return err; | |
316670eb A |
176 | } |
177 | ||
316670eb A |
178 | /* random misc-ish functions */ |
179 | uint32_t | |
180 | kperf_get_thread_bits( thread_t thread ) | |
181 | { | |
182 | return thread->t_chud; | |
183 | } | |
184 | ||
185 | void | |
186 | kperf_set_thread_bits( thread_t thread, uint32_t bits ) | |
187 | { | |
188 | thread->t_chud = bits; | |
189 | } | |
190 | ||
191 | /* mark an AST to fire on a thread */ | |
192 | void | |
193 | kperf_set_thread_ast( thread_t thread ) | |
194 | { | |
195 | /* FIXME: only call this on current thread from an interrupt | |
196 | * handler for now... | |
197 | */ | |
198 | if( thread != current_thread() ) | |
199 | panic( "unsafe AST set" ); | |
200 | ||
201 | act_set_kperf(thread); | |
202 | } | |
203 | ||
204 | unsigned | |
205 | kperf_sampling_status(void) | |
206 | { | |
207 | return sampling_status; | |
208 | } | |
209 | ||
210 | int | |
211 | kperf_sampling_enable(void) | |
212 | { | |
213 | /* already running! */ | |
214 | if( sampling_status == KPERF_SAMPLING_ON ) | |
215 | return 0; | |
216 | ||
217 | if ( sampling_status != KPERF_SAMPLING_OFF ) | |
218 | panic( "kperf: sampling wasn't off" ); | |
219 | ||
220 | /* make sure interrupt tables and actions are initted */ | |
221 | if( !kperf_initted | |
222 | || (kperf_action_get_count() == 0) ) | |
223 | return ECANCELED; | |
224 | ||
225 | /* mark as running */ | |
226 | sampling_status = KPERF_SAMPLING_ON; | |
227 | ||
228 | /* tell timers to enable */ | |
229 | kperf_timer_go(); | |
230 | ||
231 | return 0; | |
232 | } | |
233 | ||
234 | int | |
235 | kperf_sampling_disable(void) | |
236 | { | |
237 | if( sampling_status != KPERF_SAMPLING_ON ) | |
238 | return 0; | |
239 | ||
240 | /* mark a shutting down */ | |
241 | sampling_status = KPERF_SAMPLING_SHUTDOWN; | |
242 | ||
243 | /* tell timers to disable */ | |
244 | kperf_timer_stop(); | |
245 | ||
246 | /* mark as off */ | |
247 | sampling_status = KPERF_SAMPLING_OFF; | |
248 | ||
249 | return 0; | |
250 | } | |
39236c6e A |
251 | |
252 | int | |
253 | kperf_port_to_pid(mach_port_name_t portname) | |
254 | { | |
255 | task_t task; | |
256 | int pid; | |
257 | ||
258 | if( !MACH_PORT_VALID(portname) ) | |
259 | return -1; | |
260 | ||
261 | task = port_name_to_task(portname); | |
262 | ||
263 | if( task == TASK_NULL ) | |
264 | return -1; | |
265 | ||
266 | ||
267 | pid = chudxnu_pid_for_task(task); | |
268 | ||
269 | task_deallocate_internal(task); | |
270 | ||
271 | return pid; | |
272 | } |