4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 1994, by Sun Microsytems, Inc.
27 * Interfaces to control kernel tracing and kernel probes
37 #include <string.h> /* for strerror() */
38 #include <sys/types.h>
46 #include "tnfctl_int.h"
47 #include "kernel_int.h"
49 /* The TNF pseudo-device */
50 #define TNFDRIVER "/dev/tnfctl"
52 /* Dummy "test" function -- just used to flag enabled probes */
53 #define PRBK_DUMMY_TEST ((tnf_probe_test_func_t) 4)
55 /* Dummy "commit" function -- just used to flag trace enabled */
56 #define PRBK_DUMMY_COMMIT ((tnf_probe_func_t) 8)
58 /* Dummy "rollback" function -- just used to flag trace disabled */
59 #define PRBK_DUMMY_ROLLBACK ((tnf_probe_func_t) 12)
61 /* Dummy "end" function */
62 #define PRBK_DUMMY_END ((uintptr_t) 16)
64 /* Dummy "alloc" function */
65 #define PRBK_DUMMY_ALLOC ((uintptr_t) 20)
67 /* Minimum and maximum allowed buffer sizes. */
68 /* XXX -- maximum should be some function of physmem. */
69 #define KERNEL_MINBUF_SIZE (128 * 1024)
70 #define KERNEL_MAXBUF_SIZE (128 * 1024 * 1024)
72 static tnfctl_errcode_t
prbk_get_buf_attrs(tnfctl_handle_t
*hdl
);
73 static tnfctl_errcode_t
alloc_probe_space(tnfctl_handle_t
*hndl
, int maxprobe
);
76 * Initialize the kernel interface: Open the TNF control device,
77 * and determine the current kernel probes state, including the
78 * current pidfilter list.
81 _tnfctl_prbk_init(tnfctl_handle_t
*hdl
)
83 tnfctl_errcode_t prexstat
;
87 kfd
= open(TNFDRIVER
, O_RDWR
);
89 return (tnfctl_status_map(errno
));
91 if (ioctl(kfd
, TIFIOCGSTATE
, &kstate
) < 0)
92 return (tnfctl_status_map(errno
));
95 hdl
->kpidfilter_state
= kstate
.pidfilter_mode
;
96 hdl
->trace_state
= !kstate
.trace_stopped
;
97 hdl
->trace_min_size
= KERNEL_MINBUF_SIZE
;
98 prexstat
= prbk_get_buf_attrs(hdl
);
102 return (TNFCTL_ERR_NONE
);
106 * Close the TNF control device.
109 _tnfctl_prbk_close(tnfctl_handle_t
*hdl
)
112 return (TNFCTL_ERR_NONE
);
114 if (close(hdl
->kfd
) == -1) {
115 return (tnfctl_status_map(errno
));
117 return (TNFCTL_ERR_NONE
);
121 * Returns function addresses that can be plugged into function pointers
122 * in kernel probes. These are actually dummy values that get
123 * interpreted by a routine in this file when a probe is flushed.
126 _tnfctl_prbk_get_other_funcs(uintptr_t *allocp
, uintptr_t *commitp
,
127 uintptr_t *rollbackp
, uintptr_t *endp
)
129 *allocp
= PRBK_DUMMY_ALLOC
;
130 *commitp
= (uintptr_t) PRBK_DUMMY_COMMIT
;
131 *rollbackp
= (uintptr_t) PRBK_DUMMY_ROLLBACK
;
132 *endp
= PRBK_DUMMY_END
;
137 * Returns test function address
140 _tnfctl_prbk_test_func(uintptr_t *outp
)
142 *outp
= (uintptr_t) PRBK_DUMMY_TEST
;
146 * Allocate a trace buffer. Check for reasonable size; reject if there's
150 _tnfctl_prbk_buffer_alloc(tnfctl_handle_t
*hdl
, int size
)
152 tifiocstate_t bufstat
;
153 tnfctl_errcode_t prexstat
;
156 if (ioctl(hdl
->kfd
, TIFIOCGSTATE
, &bufstat
) < 0) {
157 return (tnfctl_status_map(errno
));
159 if (bufstat
.buffer_state
!= TIFIOCBUF_NONE
) {
160 return (TNFCTL_ERR_BUFEXISTS
);
162 if (size
< KERNEL_MINBUF_SIZE
) {
163 return (TNFCTL_ERR_SIZETOOSMALL
);
164 } else if (size
> KERNEL_MAXBUF_SIZE
) {
165 /* REMIND: make this an error ? */
166 size
= KERNEL_MAXBUF_SIZE
;
168 if (ioctl(hdl
->kfd
, TIFIOCALLOCBUF
, size
) < 0) {
170 (void) prbk_get_buf_attrs(hdl
);
171 return (tnfctl_status_map(saved_val
));
174 prexstat
= prbk_get_buf_attrs(hdl
);
178 return (TNFCTL_ERR_NONE
);
182 * Deallocate the kernel's trace buffer.
185 _tnfctl_prbk_buffer_dealloc(tnfctl_handle_t
*hdl
)
187 tifiocstate_t bufstat
;
188 tnfctl_errcode_t prexstat
;
191 if (ioctl(hdl
->kfd
, TIFIOCGSTATE
, &bufstat
) < 0) {
192 return (tnfctl_status_map(errno
));
194 if (bufstat
.buffer_state
== TIFIOCBUF_NONE
) {
195 return (TNFCTL_ERR_NOBUF
);
198 if (bufstat
.buffer_state
== TIFIOCBUF_OK
&& !bufstat
.trace_stopped
) {
199 return (TNFCTL_ERR_BADDEALLOC
);
201 if (ioctl(hdl
->kfd
, TIFIOCDEALLOCBUF
) < 0) {
203 (void) prbk_get_buf_attrs(hdl
);
204 return (tnfctl_status_map(saved_val
));
207 prexstat
= prbk_get_buf_attrs(hdl
);
211 return (TNFCTL_ERR_NONE
);
215 * Turns kernel global tracing on or off.
218 _tnfctl_prbk_set_tracing(tnfctl_handle_t
*hdl
, boolean_t onoff
)
220 if (hdl
->trace_state
!= onoff
&&
221 ioctl(hdl
->kfd
, TIFIOCSTRACING
, onoff
) < 0) {
222 if (errno
== ENOMEM
&& onoff
)
223 return (TNFCTL_ERR_NOBUF
);
225 return (tnfctl_status_map(errno
));
227 hdl
->trace_state
= onoff
;
228 return (TNFCTL_ERR_NONE
);
232 * Turn process filter mode on or off. The process filter is maintained
233 * even when process filtering is off, but has no effect: all processes
237 _tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t
*hdl
, boolean_t onoff
)
239 if (hdl
->kpidfilter_state
!= onoff
&&
240 ioctl(hdl
->kfd
, TIFIOCSPIDFILTER
, onoff
) < 0) {
241 return (tnfctl_status_map(errno
));
243 hdl
->kpidfilter_state
= onoff
;
244 return (TNFCTL_ERR_NONE
);
248 * Return the process filter list.
251 _tnfctl_prbk_get_pfilter_list(tnfctl_handle_t
*hdl
, pid_t
**ret_list_p
,
254 tifiocstate_t kstate
;
259 if (ioctl(hdl
->kfd
, TIFIOCGSTATE
, &kstate
) < 0)
260 return (tnfctl_status_map(errno
));
262 if (kstate
.pidfilter_size
== 0) {
265 return (TNFCTL_ERR_NONE
);
268 filterset
= (int *) malloc((kstate
.pidfilter_size
+ 1) *
270 if (filterset
== NULL
)
271 return (TNFCTL_ERR_ALLOCFAIL
);
272 if (ioctl(hdl
->kfd
, TIFIOCPIDFILTERGET
, filterset
) < 0)
273 return (tnfctl_status_map(errno
));
275 /* filterset[0] contains size of array */
276 ret_list
= malloc(filterset
[0] * sizeof (pid_t
));
277 if (ret_list
== NULL
)
278 return (TNFCTL_ERR_ALLOCFAIL
);
280 for (i
= 1; i
<= filterset
[0]; ++i
)
281 ret_list
[i
- 1] = filterset
[i
];
283 *ret_count
= filterset
[0];
284 (void) free(filterset
);
285 *ret_list_p
= ret_list
;
286 return (TNFCTL_ERR_NONE
);
290 * Add the pid to the process filter list.
291 * check whether it's already in the filter list,
292 * and whether the process exists.
295 _tnfctl_prbk_pfilter_add(tnfctl_handle_t
*hdl
, pid_t pid_to_add
)
297 if (ioctl(hdl
->kfd
, TIFIOCSPIDON
, pid_to_add
) < 0) {
298 return (tnfctl_status_map(errno
));
300 return (TNFCTL_ERR_NONE
);
304 * Drop the pid from the process filter list.
307 _tnfctl_prbk_pfilter_delete(tnfctl_handle_t
*hdl
, pid_t pid_to_del
)
309 if (ioctl(hdl
->kfd
, TIFIOCSPIDOFF
, pid_to_del
) < 0) {
310 if (errno
== ESRCH
) {
311 return (TNFCTL_ERR_NOPROCESS
);
313 return (tnfctl_status_map(errno
));
316 return (TNFCTL_ERR_NONE
);
320 * get the buffer attributes - side effect tnfctl handle
322 static tnfctl_errcode_t
323 prbk_get_buf_attrs(tnfctl_handle_t
*hdl
)
325 tifiocstate_t bufstat
;
327 if (ioctl(hdl
->kfd
, TIFIOCGSTATE
, &bufstat
) < 0) {
328 return (tnfctl_status_map(errno
));
331 hdl
->trace_file_name
= NULL
;
332 hdl
->trace_buf_size
= bufstat
.buffer_size
;
333 if (bufstat
.buffer_state
== TIFIOCBUF_NONE
)
334 hdl
->trace_buf_state
= TNFCTL_BUF_NONE
;
335 else if (bufstat
.buffer_state
== TIFIOCBUF_BROKEN
)
336 hdl
->trace_buf_state
= TNFCTL_BUF_BROKEN
;
338 hdl
->trace_buf_state
= TNFCTL_BUF_OK
;
339 return (TNFCTL_ERR_NONE
);
343 * "Flush" a probe: i.e., sync up the kernel state with the
344 * (desired) state stored in our data structure.
347 _tnfctl_prbk_flush(tnfctl_handle_t
*hndl
, prbctlref_t
*p
)
349 tnf_probevals_t probebuf
;
351 probebuf
.probenum
= p
->probe_id
;
352 probebuf
.enabled
= (p
->wrkprbctl
.test_func
!= NULL
);
353 probebuf
.traced
= (p
->wrkprbctl
.commit_func
== PRBK_DUMMY_COMMIT
);
354 if (ioctl(hndl
->kfd
, TIFIOCSPROBEVALS
, &probebuf
) < 0)
355 return (tnfctl_status_map(errno
));
356 return (TNFCTL_ERR_NONE
);
360 * Refresh our understanding of the existing probes in the kernel.
363 _tnfctl_refresh_kernel(tnfctl_handle_t
*hndl
)
367 tnfctl_errcode_t prexstat
;
368 tnf_probevals_t probebuf
;
370 prbctlref_t
*p
= NULL
;
372 prexstat
= prbk_get_buf_attrs(hndl
);
376 * Here is where you'd set obj_p->new to B_FALSE and obj_p->old to
377 * B_TRUE for all existing objects. We currently don't need
378 * it until we get modload/unload working correctly with probes
380 if (ioctl(hndl
->kfd
, TIFIOCGMAXPROBE
, &maxprobe
) < 0)
381 return (tnfctl_status_map(errno
));
382 if (maxprobe
== hndl
->num_probes
) {
383 /* XXX Inadequate in the presence of module unloading */
384 return (TNFCTL_ERR_NONE
);
387 prexstat
= alloc_probe_space(hndl
, maxprobe
);
391 obj_p
= hndl
->objlist
;
392 assert((obj_p
!= NULL
) && (obj_p
->probes
!= NULL
));
394 for (i
= 1; i
<= maxprobe
; ++i
) {
396 if (i
>= (obj_p
->min_probe_num
+ obj_p
->probecnt
)) {
400 /* make sure we are in the correct object */
401 assert(obj_p
!= NULL
);
402 assert((i
>= obj_p
->min_probe_num
) &&
403 (i
< (obj_p
->min_probe_num
+ obj_p
->probecnt
)));
405 /* get a pointer to correct probe */
406 pos
= i
- obj_p
->min_probe_num
;
407 p
= &(obj_p
->probes
[pos
]);
408 assert((p
!= NULL
) && (p
->probe_id
== i
) && (p
->probe_handle
));
410 probebuf
.probenum
= i
;
411 if (ioctl(hndl
->kfd
, TIFIOCGPROBEVALS
, &probebuf
) < 0) {
412 if (errno
== ENOENT
) {
414 * This probe has vanished due to a module
417 p
->probe_handle
->valid
= B_FALSE
;
419 return (tnfctl_status_map(errno
));
422 if (p
->probe_handle
->valid
== B_FALSE
) {
424 * seeing this probe for the first time
425 * (alloc_probe_space() initialized this
426 * "valid" field to B_FALSE)
428 /* Update our info about this probe */
429 p
->wrkprbctl
.test_func
= (probebuf
.enabled
) ?
430 PRBK_DUMMY_TEST
: NULL
;
431 p
->wrkprbctl
.commit_func
= (probebuf
.traced
) ?
432 PRBK_DUMMY_COMMIT
: PRBK_DUMMY_ROLLBACK
;
433 p
->probe_handle
->valid
= B_TRUE
;
434 if (probebuf
.attrsize
< sizeof (probebuf
))
435 probebuf
.attrsize
= sizeof (probebuf
);
436 p
->attr_string
= malloc(probebuf
.attrsize
);
437 if (p
->attr_string
== NULL
)
438 return (TNFCTL_ERR_ALLOCFAIL
);
440 * NOTE: the next statement is a structure
441 * copy and *not* a pointer assignment
443 /* LINTED pointer cast may result in improper alignment */
444 *(tnf_probevals_t
*) p
->attr_string
= probebuf
;
445 if (ioctl(hndl
->kfd
, TIFIOCGPROBESTRING
,
447 return (tnfctl_status_map(errno
));
448 if (hndl
->create_func
) {
449 p
->probe_handle
->client_registered_data
=
450 hndl
->create_func(hndl
,
456 hndl
->num_probes
= maxprobe
;
457 return (TNFCTL_ERR_NONE
);
461 * check if there are any new probes in the kernel that we aren't aware of.
462 * If so, allocate space for those probes in our data structure.
464 static tnfctl_errcode_t
465 alloc_probe_space(tnfctl_handle_t
*hndl
, int maxprobe
)
468 objlist_t
*obj_p
, *nobj_p
;
469 int min_probe_num
, i
;
470 prbctlref_t
*probe_p
;
472 /* we know that: hndl->maxprobe != maxprobe */
473 obj_p
= hndl
->objlist
;
475 /* no objects allocated */
476 o_pp
= &(hndl
->objlist
);
479 /* find last object */
480 while (obj_p
->next
!= NULL
) {
481 /* reset new_probe field on modload/unload */
482 obj_p
->new_probe
= B_FALSE
;
485 o_pp
= &(obj_p
->next
);
486 min_probe_num
= obj_p
->min_probe_num
+ obj_p
->probecnt
;
489 nobj_p
= calloc(1, sizeof (objlist_t
));
491 return (TNFCTL_ERR_ALLOCFAIL
);
492 /* add to the linked list */
494 /* NULL, B_FALSE, or 0's not explicitly initialized */
495 nobj_p
->new_probe
= B_TRUE
;
496 nobj_p
->new = B_TRUE
;
498 nobj_p
->min_probe_num
= min_probe_num
;
499 nobj_p
->probecnt
= maxprobe
- min_probe_num
+ 1;
500 nobj_p
->probes
= calloc(nobj_p
->probecnt
, sizeof (prbctlref_t
));
501 if (nobj_p
->probes
== NULL
) {
503 return (TNFCTL_ERR_ALLOCFAIL
);
506 probe_p
= &(nobj_p
->probes
[0]);
507 for (i
= min_probe_num
; i
<= maxprobe
; i
++) {
508 probe_p
->obj
= nobj_p
;
509 probe_p
->probe_id
= i
;
510 probe_p
->probe_handle
= calloc(1, sizeof (tnfctl_probe_t
));
511 if (probe_p
->probe_handle
== NULL
) {
513 free(nobj_p
->probes
);
515 return (TNFCTL_ERR_ALLOCFAIL
);
517 probe_p
->probe_handle
->valid
= B_FALSE
;
518 probe_p
->probe_handle
->probe_p
= probe_p
;
519 /* link in probe handle into chain off tnfctl_handle_t */
520 probe_p
->probe_handle
->next
= hndl
->probe_handle_list_head
;
521 hndl
->probe_handle_list_head
= probe_p
->probe_handle
;
526 hndl
->num_probes
= maxprobe
;
527 return (TNFCTL_ERR_NONE
);