8366 remove warlock leftovers from usr/src/cmd and usr/src/lib
[unleashed.git] / usr / src / lib / libtnfctl / kernel_int.c
blob958044091932b97cc38871404f0e3a410ee36835
1 /*
2 * CDDL HEADER START
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
7 * with the License.
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]
20 * CDDL HEADER END
23 * Copyright (c) 1994, by Sun Microsytems, Inc.
27 * Interfaces to control kernel tracing and kernel probes
30 #ifndef DEBUG
31 #define NDEBUG 1
32 #endif
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h> /* for strerror() */
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/tnf.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <signal.h>
44 #include <assert.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.
80 tnfctl_errcode_t
81 _tnfctl_prbk_init(tnfctl_handle_t *hdl)
83 tnfctl_errcode_t prexstat;
84 tifiocstate_t kstate;
85 int kfd;
87 kfd = open(TNFDRIVER, O_RDWR);
88 if (kfd < 0) {
89 return (tnfctl_status_map(errno));
91 if (ioctl(kfd, TIFIOCGSTATE, &kstate) < 0)
92 return (tnfctl_status_map(errno));
94 hdl->kfd = kfd;
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);
99 if (prexstat)
100 return (prexstat);
102 return (TNFCTL_ERR_NONE);
106 * Close the TNF control device.
108 tnfctl_errcode_t
109 _tnfctl_prbk_close(tnfctl_handle_t *hdl)
111 if (hdl == NULL)
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.
125 void
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
139 void
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
147 * already a buffer.
149 tnfctl_errcode_t
150 _tnfctl_prbk_buffer_alloc(tnfctl_handle_t *hdl, int size)
152 tifiocstate_t bufstat;
153 tnfctl_errcode_t prexstat;
154 int saved_val;
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) {
169 saved_val = errno;
170 (void) prbk_get_buf_attrs(hdl);
171 return (tnfctl_status_map(saved_val));
174 prexstat = prbk_get_buf_attrs(hdl);
175 if (prexstat)
176 return (prexstat);
178 return (TNFCTL_ERR_NONE);
182 * Deallocate the kernel's trace buffer.
184 tnfctl_errcode_t
185 _tnfctl_prbk_buffer_dealloc(tnfctl_handle_t *hdl)
187 tifiocstate_t bufstat;
188 tnfctl_errcode_t prexstat;
189 int saved_val;
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) {
202 saved_val = errno;
203 (void) prbk_get_buf_attrs(hdl);
204 return (tnfctl_status_map(saved_val));
207 prexstat = prbk_get_buf_attrs(hdl);
208 if (prexstat)
209 return (prexstat);
211 return (TNFCTL_ERR_NONE);
215 * Turns kernel global tracing on or off.
217 tnfctl_errcode_t
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);
224 else
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
234 * are traced.
236 tnfctl_errcode_t
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.
250 tnfctl_errcode_t
251 _tnfctl_prbk_get_pfilter_list(tnfctl_handle_t *hdl, pid_t **ret_list_p,
252 int *ret_count)
254 tifiocstate_t kstate;
255 int *filterset;
256 int i;
257 pid_t *ret_list;
259 if (ioctl(hdl->kfd, TIFIOCGSTATE, &kstate) < 0)
260 return (tnfctl_status_map(errno));
262 if (kstate.pidfilter_size == 0) {
263 *ret_count = 0;
264 *ret_list_p = NULL;
265 return (TNFCTL_ERR_NONE);
268 filterset = (int *) malloc((kstate.pidfilter_size + 1) *
269 sizeof (pid_t));
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.
294 tnfctl_errcode_t
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.
306 tnfctl_errcode_t
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);
312 } else {
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;
337 else
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.
346 tnfctl_errcode_t
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.
362 tnfctl_errcode_t
363 _tnfctl_refresh_kernel(tnfctl_handle_t *hndl)
365 int maxprobe, i;
366 int pos;
367 tnfctl_errcode_t prexstat;
368 tnf_probevals_t probebuf;
369 objlist_t *obj_p;
370 prbctlref_t *p = NULL;
372 prexstat = prbk_get_buf_attrs(hndl);
373 if (prexstat)
374 return (prexstat);
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);
388 if (prexstat)
389 return (prexstat);
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)) {
397 obj_p = obj_p->next;
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
415 * unload.
417 p->probe_handle->valid = B_FALSE;
418 } else {
419 return (tnfctl_status_map(errno));
421 } else {
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,
446 p->attr_string) < 0)
447 return (tnfctl_status_map(errno));
448 if (hndl->create_func) {
449 p->probe_handle->client_registered_data =
450 hndl->create_func(hndl,
451 p->probe_handle);
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)
467 objlist_t **o_pp;
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;
474 if (obj_p == NULL) {
475 /* no objects allocated */
476 o_pp = &(hndl->objlist);
477 min_probe_num = 1;
478 } else {
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;
483 obj_p = obj_p->next;
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));
490 if (nobj_p == NULL)
491 return (TNFCTL_ERR_ALLOCFAIL);
492 /* add to the linked list */
493 *o_pp = nobj_p;
494 /* NULL, B_FALSE, or 0's not explicitly initialized */
495 nobj_p->new_probe = B_TRUE;
496 nobj_p->new = B_TRUE;
497 nobj_p->objfd = -1;
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) {
502 free(nobj_p);
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) {
512 if (nobj_p->probes)
513 free(nobj_p->probes);
514 free(nobj_p);
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;
523 probe_p++;
526 hndl->num_probes = maxprobe;
527 return (TNFCTL_ERR_NONE);