]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_kpc.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / kern / kern_kpc.c
CommitLineData
39236c6e
A
1/*
2 * Copyright (c) 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 <kern/debug.h>
30#include <sys/param.h>
31#include <sys/mman.h>
32#include <sys/stat.h>
33#include <sys/sysctl.h>
34#include <libkern/libkern.h>
35#include <kern/assert.h>
36
37#include <kern/kpc.h>
38
39#include <pexpert/pexpert.h>
40
41/* Various sysctl requests */
42#define REQ_CLASSES (1)
43#define REQ_COUNTING (2)
44#define REQ_THREAD_COUNTING (3)
45#define REQ_CONFIG_COUNT (4)
46#define REQ_COUNTER_COUNT (5)
47#define REQ_THREAD_COUNTERS (6)
48#define REQ_COUNTERS (7)
49#define REQ_SHADOW_COUNTERS (8)
50#define REQ_CONFIG (9)
51#define REQ_PERIOD (10)
52#define REQ_ACTIONID (11)
53#define REQ_FORCE_ALL_CTRS (12)
54#define REQ_DISABLE_WHITELIST (13)
55
56/* Type-munging casts */
57typedef int (*getint_t)(void);
58typedef int (*setint_t)(int);
59
60/* safety */
61static int kpc_initted = 0;
62
63/* locking and buffer for large data requests */
64static lck_grp_attr_t *sysctl_buffer_lckgrp_attr = NULL;
65static lck_grp_t *sysctl_buffer_lckgrp = NULL;
66static lck_mtx_t sysctl_buffer_lock;
67static void *sysctl_buffer = NULL;
68
69typedef int (*setget_func_t)(int);
70
71/* init our stuff */
72extern void kpc_thread_init(void); /* osfmk/kern/kpc_thread.c */
73extern void kpc_arch_init(void);
74
75void
76kpc_init(void)
77{
78 sysctl_buffer_lckgrp_attr = lck_grp_attr_alloc_init();
79 sysctl_buffer_lckgrp = lck_grp_alloc_init("kpc",
80 sysctl_buffer_lckgrp_attr);
81 lck_mtx_init(&sysctl_buffer_lock, sysctl_buffer_lckgrp, LCK_ATTR_NULL);
82
83 kpc_arch_init();
84 kpc_thread_init();
85
86 kpc_initted = 1;
87}
88
89/* abstract sysctl handlers */
90static int
91sysctl_get_int( struct sysctl_oid *oidp, struct sysctl_req *req,
92 uint32_t value )
93{
94 int error = 0;
95
96 /* copy out the old value */
97 error = sysctl_handle_int(oidp, &value, 0, req);
98
99 return error;
100}
101
102static int
103sysctl_getset_int( struct sysctl_oid *oidp, struct sysctl_req *req,
104 int (*get_func)(void), int (*set_func)(int) )
105{
106 int error = 0;
107 uint32_t value = 0;
108
109 /* get the old value and process it */
110 value = get_func();
111
112 /* copy out the old value, get the new value */
113 error = sysctl_handle_int(oidp, &value, 0, req);
114 if (error || !req->newptr)
115 return (error);
116
117 /* if that worked, and we're writing... */
118 error = set_func( value );
119
120 return error;
121}
122
123static int
124sysctl_setget_int( struct sysctl_req *req,
125 int (*setget_func)(int) )
126{
127 int error = 0;
128 int value = 0;
129
130 error = SYSCTL_IN( req, &value, sizeof(value) );
131 if( error )
132 return error;
133
134 value = setget_func(value);
135
136 error = SYSCTL_OUT( req, &value, sizeof(value) );
137
138 return error;
139}
140
141static int
142kpc_sysctl_acquire_buffer(void)
143{
144 if( sysctl_buffer == NULL )
145 sysctl_buffer = kpc_counterbuf_alloc();
146
147 if( !sysctl_buffer )
148 {
149 return ENOMEM;
150 }
151
152 return 0;
153}
154
155static int
156sysctl_kpc_get_counters(uint32_t counters,
157 uint32_t *size, void *buf)
158{
159 uint64_t *ctr_buf = (uint64_t*)buf;
160 int curcpu;
161 uint32_t count;
162
163 count = kpc_get_cpu_counters(counters & KPC_ALL_CPUS,
164 counters,
165 &curcpu, &ctr_buf[1]);
166 if (!count)
167 return EINVAL;
168
169 ctr_buf[0] = curcpu;
170
171 *size = (count+1) * sizeof(uint64_t);
172
173 return 0;
174}
175
176static int
177sysctl_kpc_get_shadow_counters(uint32_t counters,
178 uint32_t *size, void *buf)
179{
180 uint64_t *ctr_buf = (uint64_t*)buf;
181 int curcpu;
182 uint32_t count;
183
184 count = kpc_get_shadow_counters(counters & KPC_ALL_CPUS,
185 counters,
186 &curcpu, &ctr_buf[1]);
187
188 if (!count)
189 return EINVAL;
190
191 ctr_buf[0] = curcpu;
192
193 *size = (count+1) * sizeof(uint64_t);
194
195 return 0;
196}
197
198static int
199sysctl_kpc_get_thread_counters(uint32_t tid,
200 uint32_t *size, void *buf)
201{
202 uint32_t count = *size / sizeof(uint64_t);
203 int r;
204
205 if( tid != 0 )
206 return EINVAL;
207
208 r = kpc_get_curthread_counters(&count, buf);
209 if( !r )
210 *size = count * sizeof(uint64_t);
211
212 return r;
213}
214
215static int
216sysctl_kpc_get_config(uint32_t classes, void* buf)
217{
218 return kpc_get_config( classes, buf );
219}
220
221static int
222sysctl_kpc_set_config(uint32_t classes, void* buf)
223{
224 return kpc_set_config( classes, buf);
225}
226
227static int
228sysctl_kpc_get_period(uint32_t classes, void* buf)
229{
230 return kpc_get_period( classes, buf );
231}
232
233static int
234sysctl_kpc_set_period(uint32_t classes, void* buf)
235{
236 return kpc_set_period( classes, buf);
237}
238
239static int
240sysctl_kpc_get_actionid(uint32_t classes, void* buf)
241{
242 return kpc_get_actionid( classes, buf );
243}
244
245static int
246sysctl_kpc_set_actionid(uint32_t classes, void* buf)
247{
248 return kpc_set_actionid( classes, buf);
249}
250
251
252static int
253sysctl_get_bigarray( struct sysctl_req *req,
254 int (*get_fn)(uint32_t, uint32_t*, void*) )
255{
256 int error = 0;
257 uint32_t bufsize = KPC_MAX_COUNTERS * sizeof(uint64_t); /* XXX? */
258 uint32_t arg = 0;
259
260 /* get the argument */
261 error = SYSCTL_IN( req, &arg, sizeof(arg) );
262 if(error)
263 {
264 printf( "kpc: no arg?\n" );
265 return error;
266 }
267
268 /* get the wired buffer */
269 error = kpc_sysctl_acquire_buffer();
270 if (error)
271 return error;
272
273 /* atomically get the array into the wired buffer. We have a double
274 * copy, but this is better than page faulting / interrupting during
275 * a copy.
276 */
277 error = get_fn( arg, &bufsize, sysctl_buffer );
278
279 /* do the copy out */
280 if( !error )
281 error = SYSCTL_OUT( req, sysctl_buffer, bufsize );
282
283 return error;
284}
285
286/* given a config word, how many bytes does it take? */
287static int
288sysctl_config_size( uint32_t config )
289{
290 return kpc_get_config_count(config) * sizeof(kpc_config_t);
291}
292
293static int
294sysctl_counter_size( uint32_t classes )
295{
296 return kpc_get_counter_count(classes) * sizeof(uint64_t);
297}
298
299static int
300sysctl_actionid_size( uint32_t classes )
301{
302 return kpc_get_counter_count(classes) * sizeof(int32_t);
303}
304
305static int
306sysctl_getset_bigarray( struct sysctl_req *req,
307 int (*size_fn)(uint32_t arg),
308 int (*get_fn)(uint32_t, void*),
309 int (*set_fn)(uint32_t, void*) )
310{
311 int error = 0;
312 uint32_t bufsize = KPC_MAX_COUNTERS * sizeof(uint64_t); /* XXX? */
313 uint32_t regsize = 0;
314 uint64_t arg;
315
316 /* get the config word */
317 error = SYSCTL_IN( req, &arg, sizeof(arg) );
318 if(error)
319 {
320 printf( "kpc: no arg?\n" );
321 return error;
322 }
323
324 /* Work out size of registers */
325 regsize = size_fn((uint32_t)arg);
326
327 /* Ignore NULL requests */
328 if(regsize == 0)
329 return EINVAL;
330
331 /* ensure not too big */
332 if( regsize > bufsize )
333 return EINVAL;
334
335 /* get the wired buffer */
336 error = kpc_sysctl_acquire_buffer();
337 if (error)
338 return error;
339
340 // if writing...
341 if(req->newptr)
342 {
343 // copy in the rest in -- sysctl remembers we did one already
344 error = SYSCTL_IN( req, sysctl_buffer,
345 regsize );
346
347 // if SYSCTL_IN fails it means we are only doing a read
348 if(!error) {
349 // set it
350 error = set_fn( (uint32_t)arg, sysctl_buffer );
351 if( error )
352 goto fail;
353 }
354 }
355
356 // if reading
357 if(req->oldptr)
358 {
359 // read it
360 error = get_fn( (uint32_t)arg, sysctl_buffer );
361 if( error )
362 goto fail;
363
364 // copy out the full set
365 error = SYSCTL_OUT( req, sysctl_buffer, regsize );
366 }
367
368fail:
369 return error;
370}
371
372
373
374/*
375 * #define SYSCTL_HANDLER_ARGS (struct sysctl_oid *oidp, \
376 * void *arg1, int arg2, \
377 * struct sysctl_req *req )
378 */
379static int
380kpc_sysctl SYSCTL_HANDLER_ARGS
381{
382 int ret;
383
384 // __unused struct sysctl_oid *unused_oidp = oidp;
385 (void)arg2;
386
387 if( !kpc_initted )
388 panic("kpc_init not called");
389
390 lck_mtx_lock(&sysctl_buffer_lock);
391
392 /* which request */
393 switch( (uintptr_t) arg1 )
394 {
395 case REQ_CLASSES:
396 ret = sysctl_get_int( oidp, req,
397 kpc_get_classes() );
398 break;
399 case REQ_COUNTING:
400 ret = sysctl_getset_int( oidp, req,
401 (getint_t)kpc_get_running,
402 (setint_t)kpc_set_running );
403 break;
404 case REQ_THREAD_COUNTING:
405 ret = sysctl_getset_int( oidp, req,
406 (getint_t)kpc_get_thread_counting,
407 (setint_t)kpc_set_thread_counting );
408 break;
409
410 case REQ_CONFIG_COUNT:
411 ret = sysctl_setget_int( req,
412 (setget_func_t)kpc_get_config_count );
413 break;
414
415 case REQ_COUNTER_COUNT:
416 ret = sysctl_setget_int( req,
417 (setget_func_t)kpc_get_counter_count );
418 break;
419
420
421 case REQ_THREAD_COUNTERS:
422 ret = sysctl_get_bigarray( req, sysctl_kpc_get_thread_counters );
423 break;
424
425 case REQ_COUNTERS:
426 ret = sysctl_get_bigarray( req, sysctl_kpc_get_counters );
427 break;
428
429 case REQ_SHADOW_COUNTERS:
430 ret = sysctl_get_bigarray( req, sysctl_kpc_get_shadow_counters );
431 break;
432
433 case REQ_CONFIG:
434 ret = sysctl_getset_bigarray( req,
435 sysctl_config_size,
436 sysctl_kpc_get_config,
437 sysctl_kpc_set_config );
438 break;
439
440 case REQ_PERIOD:
441 ret = sysctl_getset_bigarray( req,
442 sysctl_counter_size,
443 sysctl_kpc_get_period,
444 sysctl_kpc_set_period );
445 break;
446
447 case REQ_ACTIONID:
448 ret = sysctl_getset_bigarray( req,
449 sysctl_actionid_size,
450 sysctl_kpc_get_actionid,
451 sysctl_kpc_set_actionid );
452 break;
453
454 default:
455 ret = ENOENT;
456 break;
457 }
458
459 lck_mtx_unlock(&sysctl_buffer_lock);
460
461 return ret;
462}
463
464
465/*** sysctl definitions ***/
466
467/* root kperf node */
468SYSCTL_NODE(, OID_AUTO, kpc, CTLFLAG_RW|CTLFLAG_LOCKED, 0,
469 "kpc");
470
471/* values */
472SYSCTL_PROC(_kpc, OID_AUTO, classes,
473 CTLTYPE_INT|CTLFLAG_RD|CTLFLAG_ANYBODY,
474 (void*)REQ_CLASSES,
475 sizeof(int), kpc_sysctl, "I", "Available classes");
476
477SYSCTL_PROC(_kpc, OID_AUTO, counting,
478 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
479 (void*)REQ_COUNTING,
480 sizeof(int), kpc_sysctl, "I", "PMCs counting");
481
482SYSCTL_PROC(_kpc, OID_AUTO, thread_counting,
483 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
484 (void*)REQ_THREAD_COUNTING,
485 sizeof(int), kpc_sysctl, "I", "Thread accumulation");
486
487/* faux values */
488SYSCTL_PROC(_kpc, OID_AUTO, config_count,
489 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
490 (void*)REQ_CONFIG_COUNT,
491 sizeof(int), kpc_sysctl, "S", "Config count");
492
493SYSCTL_PROC(_kpc, OID_AUTO, counter_count,
494 CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
495 (void*)REQ_COUNTER_COUNT,
496 sizeof(int), kpc_sysctl, "S", "Counter count");
497
498/* arrays */
499SYSCTL_PROC(_kpc, OID_AUTO, thread_counters,
500 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
501 (void*)REQ_THREAD_COUNTERS,
502 sizeof(uint64_t), kpc_sysctl,
503 "QU", "Current thread counters");
504
505SYSCTL_PROC(_kpc, OID_AUTO, counters,
506 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
507 (void*)REQ_COUNTERS,
508 sizeof(uint64_t), kpc_sysctl,
509 "QU", "Current counters");
510
511SYSCTL_PROC(_kpc, OID_AUTO, shadow_counters,
512 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
513 (void*)REQ_SHADOW_COUNTERS,
514 sizeof(uint64_t), kpc_sysctl,
515 "QU", "Current shadow counters");
516
517SYSCTL_PROC(_kpc, OID_AUTO, config,
518 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
519 (void*)REQ_CONFIG,
520 sizeof(uint64_t), kpc_sysctl,
521 "QU", "Set counter configs");
522
523SYSCTL_PROC(_kpc, OID_AUTO, period,
524 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
525 (void*)REQ_PERIOD,
526 sizeof(uint64_t), kpc_sysctl,
527 "QU", "Set counter periods");
528
529SYSCTL_PROC(_kpc, OID_AUTO, actionid,
530 CTLFLAG_RD|CTLFLAG_WR|CTLFLAG_ANYBODY,
531 (void*)REQ_ACTIONID,
532 sizeof(uint32_t), kpc_sysctl,
533 "QU", "Set counter actionids");